Merge lp:~keithsalisbury/mixxx/track_selector_feature into lp:~mixxxdevelopers/mixxx/trunk

Proposed by globalkeith
Status: Needs review
Proposed branch: lp:~keithsalisbury/mixxx/track_selector_feature
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 7954 lines (+7431/-2)
75 files modified
mixxx/build/depends.py (+6/-0)
mixxx/lib/replaygain/replaygain_analysis.h (+58/-0)
mixxx/res/keyboard/Standard.kbd.cfg (+101/-0)
mixxx/res/keyboard/en_US.kbd.cfg (+2/-2)
mixxx/res/mixxx.qrc (+1/-0)
mixxx/src/analyserwavesummary.cpp (+81/-0)
mixxx/src/analyserwavesummary.h (+25/-0)
mixxx/src/dlgautodj.cpp (+3/-0)
mixxx/src/dlgautodj.h (+1/-0)
mixxx/src/dlgmidilearning.cpp (+210/-0)
mixxx/src/dlgmidilearning.h (+56/-0)
mixxx/src/dlgmidilearning.ui (+241/-0)
mixxx/src/dlgprefmidibindings.cpp (+510/-0)
mixxx/src/dlgprefmidibindings.h (+92/-0)
mixxx/src/dlgprefmidibindingsdlg.ui (+324/-0)
mixxx/src/dlgprefnomidi.cpp (+25/-0)
mixxx/src/dlgprefnomidi.h (+33/-0)
mixxx/src/dlgprefnomididlg.ui (+46/-0)
mixxx/src/dlgprepare.cpp (+4/-0)
mixxx/src/dlgprepare.h (+1/-0)
mixxx/src/dlgrecording.cpp (+4/-0)
mixxx/src/dlgrecording.h (+1/-0)
mixxx/src/dlgselector.cpp (+196/-0)
mixxx/src/dlgselector.h (+67/-0)
mixxx/src/dlgselector.ui (+155/-0)
mixxx/src/library/bundledsongswebview.cpp (+4/-0)
mixxx/src/library/bundledsongswebview.h (+1/-0)
mixxx/src/library/featuredartistswebview.h (+1/-0)
mixxx/src/library/library.cpp (+2/-0)
mixxx/src/library/libraryview.h (+1/-0)
mixxx/src/library/selector/selectorfeature.cpp (+92/-0)
mixxx/src/library/selector/selectorfeature.h (+58/-0)
mixxx/src/library/selector/selectorlibrarytablemodel.cpp (+390/-0)
mixxx/src/library/selector/selectorlibrarytablemodel.h (+103/-0)
mixxx/src/mixxxcontrol.cpp (+183/-0)
mixxx/src/mixxxcontrol.h (+88/-0)
mixxx/src/softtakeover.cpp (+115/-0)
mixxx/src/softtakeover.h (+50/-0)
mixxx/src/test/analyserwavesummarytest.cpp (+102/-0)
mixxx/src/test/midiscriptenginetest.cpp (+95/-0)
mixxx/src/track/beatmatrix.cpp (+275/-0)
mixxx/src/track/beatmatrix.h (+67/-0)
mixxx/src/waveform/glwaveformrenderer.cpp (+334/-0)
mixxx/src/waveform/glwaveformrenderer.h (+59/-0)
mixxx/src/waveform/renderobject.cpp (+12/-0)
mixxx/src/waveform/renderobject.h (+27/-0)
mixxx/src/waveform/waveformrenderbackground.cpp (+70/-0)
mixxx/src/waveform/waveformrenderbackground.h (+38/-0)
mixxx/src/waveform/waveformrenderbeat.cpp (+183/-0)
mixxx/src/waveform/waveformrenderbeat.h (+53/-0)
mixxx/src/waveform/waveformrenderer.cpp (+600/-0)
mixxx/src/waveform/waveformrenderer.h (+96/-0)
mixxx/src/waveform/waveformrendermark.cpp (+315/-0)
mixxx/src/waveform/waveformrendermark.h (+68/-0)
mixxx/src/waveform/waveformrendermarkrange.cpp (+233/-0)
mixxx/src/waveform/waveformrendermarkrange.h (+62/-0)
mixxx/src/waveform/waveformrendersignal.cpp (+132/-0)
mixxx/src/waveform/waveformrendersignal.h (+49/-0)
mixxx/src/waveform/waveformrendersignalpixmap.cpp (+153/-0)
mixxx/src/waveform/waveformrendersignalpixmap.h (+46/-0)
mixxx/src/waveform/waveformrendersignaltiles.cpp (+169/-0)
mixxx/src/waveform/waveformrendersignaltiles.h (+51/-0)
mixxx/src/waveformviewerfactory.cpp (+174/-0)
mixxx/src/waveformviewerfactory.h (+46/-0)
mixxx/src/widget/wglwaveformviewer.cpp (+228/-0)
mixxx/src/widget/wglwaveformviewer.h (+75/-0)
mixxx/src/widget/wlibrary.cpp (+1/-0)
mixxx/src/widget/wlibrarytextbrowser.cpp (+4/-0)
mixxx/src/widget/wlibrarytextbrowser.h (+1/-0)
mixxx/src/widget/wselectorlibrarytableview.cpp (+27/-0)
mixxx/src/widget/wselectorlibrarytableview.h (+25/-0)
mixxx/src/widget/wtracktableview.cpp (+3/-0)
mixxx/src/widget/wtracktableview.h (+1/-0)
mixxx/src/widget/wvisualsimple.cpp (+156/-0)
mixxx/src/widget/wvisualsimple.h (+70/-0)
To merge this branch: bzr merge lp:~keithsalisbury/mixxx/track_selector_feature
Reviewer Review Type Date Requested Status
Mixxx Development Team Pending
Review via email: mp+107552@code.launchpad.net

Description of the change

Implments a new library view called Selector which enables the user to filter the library using the currently active track details with controls like 'genre', 'bpm', 'year', 'rating', 'key'. Probably the biggest impact it has on other code is to enable a onHide method so it can activate and deactivate - which enables the auto-selection to only occur when the view is visible.

For a screen grab here:

http://www.mixxx.org/forums/viewtopic.php?f=1&t=3448&start=10#p14226

To post a comment you must log in.
3017. By globalkeith

merged with trunk

3018. By globalkeith

merged with trunk

3019. By globalkeith

disable the filter controls when the filter value is not available in the currently playing track meta data

3020. By globalkeith

merge with trunk

3021. By globalkeith

merge from trunk

Unmerged revisions

3021. By globalkeith

merge from trunk

3020. By globalkeith

merge with trunk

3019. By globalkeith

disable the filter controls when the filter value is not available in the currently playing track meta data

3018. By globalkeith

merged with trunk

3017. By globalkeith

merged with trunk

3016. By globalkeith

good ol clean up, and separated the harmonics into separate checkboxes

3015. By globalkeith

------------
This line and the following will be ignored --------------

removed:
  mixxx/lib/replaygain/replaygain_analysis.c
  mixxx/lib/replaygain/replaygain_analysis.h
  mixxx/res/images/library/ic_library_setlog.png
  mixxx/res/images/library/ic_library_setlog_current.png
  mixxx/src/analysergainvamp.cpp
  mixxx/src/analysergainvamp.h
  mixxx/vamp-plugins/plugins/replaygain.cpp
  mixxx/vamp-plugins/plugins/replaygain.h
added:
  mixxx/lib/replaygain/replaygain.cpp
  mixxx/lib/replaygain/replaygain.h
  mixxx/res/controllers/common-hid-packet-parser.js
  mixxx/res/images/library/ic_library_history.png
  mixxx/res/images/library/ic_library_history_current.png
  mixxx/res/keyboard/cs_CZ.kbd.cfg
  mixxx/res/keyboard/da_DK.kbd.cfg
  mixxx/res/keyboard/de_DE.kbd.cfg
  mixxx/res/keyboard/el_GR.kbd.cfg
  mixxx/res/keyboard/es_ES.kbd.cfg
  mixxx/res/keyboard/fi_FI.kbd.cfg
  mixxx/res/keyboard/fr_FR.kbd.cfg
  mixxx/res/keyboard/it_IT.kbd.cfg
  mixxx/res/keyboard/ru_RU.kbd.cfg
  mixxx/res/skins/Deere1280x1024-SXGA/CHANGELOG.txt
  mixxx/res/skins/Deere1280x800-WXGA/CHANGELOG.txt
  mixxx/res/skins/Deere1440x900-WXGA+/CHANGELOG.txt
  mixxx/res/skins/Deere1920x1080-FullHD/CHANGELOG.txt
  mixxx/res/skins/Deere1920x1200-WUXGA/CHANGELOG.txt
  mixxx/res/skins/LateNight1280x1024-SXGA/CHANGELOG.txt
  mixxx/res/skins/LateNight1280x800-WXGA/CHANGELOG.txt
  mixxx/res/skins/LateNight1366x768-WXGA/CHANGELOG.txt
  mixxx/res/skins/LateNightBlues1280x1024-SXGA/CHANGELOG.txt
  mixxx/res/skins/LateNightBlues1280x800-WXGA/CHANGELOG.txt
  mixxx/res/skins/LateNightBlues1366x768-WXGA/CHANGELOG.txt
  mixxx/res/skins/Outline1024x600-Netbook/CHANGELOG.txt
  mixxx/res/skins/Outline1024x768-XGA/CHANGELOG.txt
  mixxx/res/skins/Outline800x480-WVGA/CHANGELOG.txt
  mixxx/res/skins/Phoney1600x1200-UXGA/CHANGELOG.txt
  mixxx/res/skins/Phoney1680x1050-WSXGA/CHANGELOG.txt
  mixxx/res/skins/PhoneyDark1600x1200-UXGA/CHANGELOG.txt
  mixxx/res/skins/PhoneyDark1680x1050-WSXGA/CHANGELOG.txt
  mixxx/res/skins/Shade1024x600-Netbook/CHANGELOG.txt
  mixxx/res/skins/Shade1024x768-XGA/CHANGELOG.txt
  mixxx/res/skins/ShadeDark1024x600-Netbook/CHANGELOG.txt
  mixxx/res/skins/ShadeDark1024x768-XGA/CHANGELOG.txt
  mixxx/res/translations/mixxx/te.po
  mixxx/res/translations/mixxx_te.qm
  mixxx/res/translations/mixxx_te.ts
  mixxx/src/controllers/hid/hidblacklist.h
  mixxx/src/library/baseexternallibraryfeature.cpp
  mixxx/src/library/baseexternallibraryfeature.h
  mixxx/src/shoutcast/
  mixxx/src/shoutcast/defs_shoutcast.h
  mixxx/src/skin/tooltips.cpp
  mixxx/src/skin/tooltips.h
  mixxx/src/waveform/renderers/qtwaveformrendererfilteredsignal.cpp
  mixxx/src/waveform/renderers/qtwaveformrendererfilteredsignal.h
  mixxx/src/waveform/renderers/qtwaveformrenderersimplesignal.cpp
  mixxx/src/waveform/renderers/qtwaveformrenderersimplesignal.h
  mixxx/src/waveform/renderers/waveformmarkset.cpp
  mixxx/src/waveform/renderers/waveformmarkset.h
  mixxx/src/waveform/renderers/waveformrenderersignalbase.cpp
  mixxx/src/waveform/renderers/waveformrenderersignalbase.h
  mixxx/src/waveform/widgets/qtsimplewaveformwidget.cpp
  mixxx/src/waveform/widgets/qtsimplewaveformwidget.h
  mixxx/src/waveform/widgets/qtwaveformwidget.cpp
  mixxx/src/waveform/widgets/qtwaveformwidget.h
renamed:
  mixxx/res/controllers/Eks Otus.cntrlr.xml => mixxx/res/controllers/EKS Otus.cntrlr.xml
  mixxx/res/keyboard/Standard.kbd.cfg => mixxx/res/keyboard/en_US.kbd.cfg
modified:
  mixxx/build/debian/mixxx.install
  mixxx/build/depends.py
  mixxx/build/features.py
  mixxx/build/nsis/Mixxx.nsi
  mixxx/lib/hidapi-0.7.0/linux/hid-libusb.c
  mixxx/lib/vamp-2.3/src/vamp-hostsdk/PluginLoader.cpp
  mixxx/res/controllers/EKS Otus.js
  mixxx/res/images/templates/ic_template_library_and_preferences.svg
  mixxx/res/mixxx.qrc
  mixxx/res/skins/Deere1280x1024-SXGA/skin.xml
  mixxx/res/skins/Deere1280x800-WXGA/skin.xml
  mixxx/res/skins/Deere1440x900-WXGA+/skin.xml
  mixxx/res/skins/Deere1920x1080-FullHD/skin.xml
  mixxx/res/skins/Deere1920x1200-WUXGA/skin.xml
  mixxx/res/skins/LateNight1280x1024-SXGA/skin.xml
  mixxx/res/skins/LateNight1280x800-WXGA/skin.xml
  mixxx/res/skins/LateNight1366x768-WXGA/skin.xml
  mixxx/res/skins/LateNightBlues1280x1024-SXGA/skin.xml
  mixxx/res/skins/LateNightBlues1280x800-WXGA/skin.xml
  mixxx/res/skins/LateNightBlues1366x768-WXGA/skin.xml
  mixxx/res/skins/Outline1024x600-Netbook/skin.xml
  mixxx/res/skins/Outline1024x768-XGA/skin.xml
  mixxx/res/skins/Outline800x480-WVGA/skin.xml
  mixxx/res/skins/Phoney1600x1200-UXGA/skin.xml
  mixxx/res/skins/Phoney1680x1050-WSXGA/skin.xml
  mixxx/res/skins/PhoneyDark1600x1200-UXGA/skin.xml
  mixxx/res/skins/PhoneyDark1680x1050-WSXGA/skin.xml
  mixxx/res/skins/Shade1024x600-Netbook/skin.xml
  mixxx/res/skins/Shade1024x768-XGA/skin.xml
  mixxx/res/skins/ShadeDark1024x600-Netbook/skin.xml
  mixxx/res/skins/ShadeDark1024x768-XGA/skin.xml
  mixxx/res/translations/mixxx.ts
  mixxx/res/translations/mixxx/ar.po
  mixxx/res/translations/mixxx/ast.po
  mixxx/res/translations/mixxx/bg.po
  mixxx/res/translations/mixxx/br.po
  mixxx/res/translations/mixxx/bs.po
  mixxx/res/translations/mixxx/ca.po
  mixxx/res/translations/mixxx/cs.po
  mixxx/res/translations/mixxx/da.po
  mixxx/res/translations/mixxx/de.po
  mixxx/res/translations/mixxx/el.po
  mixxx/res/translations/mixxx/en_GB.po
  mixxx/res/translations/mixxx/eo.po
  mixxx/res/translations/mixxx/es.po
  mixxx/res/translations/mixxx/et.po
  mixxx/res/translations/mixxx/eu.po
  mixxx/res/translations/mixxx/fi.po
  mixxx/res/translations/mixxx/fr.po
  mixxx/res/translations/mixxx/ga.po
  mixxx/res/translations/mixxx/gl.po
  mixxx/res/translations/mixxx/he.po
  mixxx/res/translations/mixxx/hu.po
  mixxx/res/translations/mixxx/hy.po
  mixxx/res/translations/mixxx/ia.po
  mixxx/res/translations/mixxx/id.po
  mixxx/res/translations/mixxx/is.po
  mixxx/res/translations/mixxx/it.po
  mixxx/res/translations/mixxx/ja.po
  mixxx/res/translations/mixxx/lb.po
  mixxx/res/translations/mixxx/mixxx.pot
  mixxx/res/translations/mixxx/ml.po
  mixxx/res/translations/mixxx/mn.po
  mixxx/res/translations/mixxx/mr.po
  mixxx/res/translations/mixxx/ms.po
  mixxx/res/translations/mixxx/nb.po
  mixxx/res/translations/mixxx/nl.po
  mixxx/res/translations/mixxx/nn.po
  mixxx/res/translations/mixxx/oc.po
  mixxx/res/translations/mixxx/pl.po
  mixxx/res/translations/mixxx/pt.po
  mixxx/res/translations/mixxx/pt_BR.po
  mixxx/res/translations/mixxx/ro.po
  mixxx/res/translations/mixxx/ru.po
  mixxx/res/translations/mixxx/sl.po
  mixxx/res/translations/mixxx/sr.po
  mixxx/res/translations/mixxx/sv.po
  mixxx/res/translations/mixxx/tr.po
  mixxx/res/translations/mixxx/uk.po
  mixxx/res/translations/mixxx/uz.po
  mixxx/res/translations/mixxx/zh_CN.po
  mixxx/res/translations/mixxx/zh_TW.po
  mixxx/res/translations/mixxx_ar.qm
  mixxx/res/translations/mixxx_ar.ts
  mixxx/res/translations/mixxx_ast.qm
  mixxx/res/translations/mixxx_ast.ts
  mixxx/res/translations/mixxx_bg.qm
  mixxx/res/translations/mixxx_bg.ts
  mixxx/res/translations/mixxx_br.qm
  mixxx/res/translations/mixxx_br.ts
  mixxx/res/translations/mixxx_bs.qm
  mixxx/res/translations/mixxx_bs.ts
  mixxx/res/translations/mixxx_ca.qm
  mixxx/res/translations/mixxx_ca.ts
  mixxx/res/translations/mixxx_cs.qm
  mixxx/res/translations/mixxx_cs.ts
  mixxx/res/translations/mixxx_da.qm
  mixxx/res/translations/mixxx_da.ts
  mixxx/res/translations/mixxx_de.qm
  mixxx/res/translations/mixxx_de.ts
  mixxx/res/translations/mixxx_el.qm
  mixxx/res/translations/mixxx_el.ts
  mixxx/res/translations/mixxx_en_GB.qm
  mixxx/res/translations/mixxx_en_GB.ts
  mixxx/res/translations/mixxx_eo.qm
  mixxx/res/translations/mixxx_eo.ts
  mixxx/res/translations/mixxx_es.qm
  mixxx/res/translations/mixxx_es.ts
  mixxx/res/translations/mixxx_et.qm
  mixxx/res/translations/mixxx_et.ts
  mixxx/res/translations/mixxx_eu.qm
  mixxx/res/translations/mixxx_eu.ts
  mixxx/res/translations/mixxx_fi.qm
  mixxx/res/translations/mixxx_fi.ts
  mixxx/res/translations/mixxx_fr.qm
  mixxx/res/translations/mixxx_fr.ts
  mixxx/res/translations/mixxx_ga.qm
  mixxx/res/translations/mixxx_ga.ts
  mixxx/res/translations/mixxx_gl.qm
  mixxx/res/translations/mixxx_gl.ts
  mixxx/res/translations/mixxx_he.qm
  mixxx/res/translations/mixxx_he.ts
  mixxx/res/translations/mixxx_hu.qm
  mixxx/res/translations/mixxx_hu.ts
  mixxx/res/translations/mixxx_hy.ts
  mixxx/res/translations/mixxx_ia.qm
  mixxx/res/translations/mixxx_ia.ts
  mixxx/res/translations/mixxx_id.qm
  mixxx/res/translations/mixxx_id.ts
  mixxx/res/translations/mixxx_is.qm
  mixxx/res/translations/mixxx_is.ts
  mixxx/res/translations/mixxx_it.qm
  mixxx/res/translations/mixxx_it.ts
  mixxx/res/translations/mixxx_ja.qm
  mixxx/res/translations/mixxx_ja.ts
  mixxx/res/translations/mixxx_lb.qm
  mixxx/res/translations/mixxx_lb.ts
  mixxx/res/translations/mixxx_ml.qm
  mixxx/res/translations/mixxx_ml.ts
  mixxx/res/translations/mixxx_mn.qm
  mixxx/res/translations/mixxx_mn.ts
  mixxx/res/translations/mixxx_mr.qm
  mixxx/res/translations/mixxx_mr.ts
  mixxx/res/translations/mixxx_ms.qm
  mixxx/res/translations/mixxx_ms.ts
  mixxx/res/translations/mixxx_nb.qm
  mixxx/res/translations/mixxx_nb.ts
  mixxx/res/translations/mixxx_nl.qm
  mixxx/res/translations/mixxx_nl.ts
  mixxx/res/translations/mixxx_nn.qm
  mixxx/res/translations/mixxx_nn.ts
  mixxx/res/translations/mixxx_oc.qm
  mixxx/res/translations/mixxx_oc.ts
  mixxx/res/translations/mixxx_pl.qm
  mixxx/res/translations/mixxx_pl.ts
  mixxx/res/translations/mixxx_pt.qm
  mixxx/res/translations/mixxx_pt.ts
  mixxx/res/translations/mixxx_pt_BR.qm
  mixxx/res/translations/mixxx_pt_BR.ts
  mixxx/res/translations/mixxx_ro.qm
  mixxx/res/translations/mixxx_ro.ts
  mixxx/res/translations/mixxx_ru.qm
  mixxx/res/translations/mixxx_ru.ts
  mixxx/res/translations/mixxx_sl.qm
  mixxx/res/translations/mixxx_sl.ts
  mixxx/res/translations/mixxx_sr.qm
  mixxx/res/translations/mixxx_sr.ts
  mixxx/res/translations/mixxx_sv.qm
  mixxx/res/translations/mixxx_sv.ts
  mixxx/res/translations/mixxx_tr.qm
  mixxx/res/translations/mixxx_tr.ts
  mixxx/res/translations/mixxx_uk.qm
  mixxx/res/translations/mixxx_uk.ts
  mixxx/res/translations/mixxx_uz.qm
  mixxx/res/translations/mixxx_uz.ts
  mixxx/res/translations/mixxx_zh_CN.qm
  mixxx/res/translations/mixxx_zh_CN.ts
  mixxx/res/translations/mixxx_zh_TW.ts
  mixxx/src/analyser.h
  mixxx/src/analyserbeats.cpp
  mixxx/src/analyserbeats.h
  mixxx/src/analyserbpm.cpp
  mixxx/src/analyserbpm.h
  mixxx/src/analyserqueue.cpp
  mixxx/src/analyserqueue.h
  mixxx/src/analyserrg.cpp
  mixxx/src/analyserrg.h
  mixxx/src/analyserwaveform.cpp
  mixxx/src/analyserwaveform.h
  mixxx/src/basetrackplayer.cpp
  mixxx/src/configobject.cpp
  mixxx/src/configobject.h
  mixxx/src/controllers/dlgcontrollerlearning.cpp
  mixxx/src/controllers/hid/hidcontroller.cpp
  mixxx/src/controllers/hid/hidcontroller.h
  mixxx/src/controllers/hid/hidenumerator.cpp
  mixxx/src/dlgautodj.cpp
  mixxx/src/dlgautodj.ui
  mixxx/src/dlgprefbeats.cpp
  mixxx/src/dlgprefbeatsdlg.ui
  mixxx/src/dlgprefcontrols.cpp
  mixxx/src/dlgprefcontrols.h
  mixxx/src/dlgprefcontrolsdlg.ui
  mixxx/src/dlgprefcrossfaderdlg.ui
  mixxx/src/dlgprefeqdlg.ui
  mixxx/src/dlgprefplaylist.cpp
  mixxx/src/dlgprefplaylistdlg.ui
  mixxx/src/dlgprefrecord.cpp
  mixxx/src/dlgprefreplaygaindlg.ui
  mixxx/src/dlgprefshoutcast.cpp
  mixxx/src/dlgprefshoutcastdlg.ui
  mixxx/src/dlgprefsounditem.cpp
  mixxx/src/dlgprefsounditem.h
  mixxx/src/dlgprepare.ui
  mixxx/src/dlgtrackinfo.cpp
  mixxx/src/dlgtrackinfo.ui
  mixxx/src/engine/cuecontrol.cpp
  mixxx/src/engine/cuecontrol.h
  mixxx/src/engine/engineshoutcast.cpp
  mixxx/src/engine/engineshoutcast.h
  mixxx/src/errordialoghandler.cpp
  mixxx/src/errordialoghandler.h
  mixxx/src/library/baseplaylistfeature.cpp
  mixxx/src/library/basesqltablemodel.cpp
  mixxx/src/library/basesqltablemodel.h
  mixxx/src/library/browse/browsefeature.cpp
  mixxx/src/library/browse/browsefeature.h
  mixxx/src/library/browse/browsethread.cpp
  mixxx/src/library/cratetablemodel.cpp
  mixxx/src/library/cratetablemodel.h
  mixxx/src/library/dao/analysisdao.cpp
  mixxx/src/library/dao/analysisdao.h
  mixxx/src/library/dao/cratedao.cpp
  mixxx/src/library/dao/libraryhashdao.cpp
  mixxx/src/library/dao/playlistdao.cpp
  mixxx/src/library/dao/playlistdao.h
  mixxx/src/library/dao/trackdao.cpp
  mixxx/src/library/dao/trackdao.h
  mixxx/src/library/itunes/itunesfeature.cpp
  mixxx/src/library/itunes/itunesfeature.h
  mixxx/src/library/itunes/itunesplaylistmodel.cpp
  mixxx/src/library/itunes/itunestrackmodel.cpp
  mixxx/src/library/library.cpp
  mixxx/src/library/libraryscanner.cpp
  mixxx/src/library/libraryscanner.h
  mixxx/src/library/librarytablemodel.cpp
  mixxx/src/library/librarytablemodel.h
  mixxx/src/library/missingtablemodel.cpp
  mixxx/src/library/missingtablemodel.h
  mixxx/src/library/playlisttablemodel.cpp
  mixxx/src/library/playlisttablemodel.h
  mixxx/src/library/proxytrackmodel.cpp
  mixxx/src/library/proxytrackmodel.h
  mixxx/src/library/queryutil.h
  mixxx/src/library/rhythmbox/rhythmboxfeature.cpp
  mixxx/src/library/rhythmbox/rhythmboxfeature.h
  mixxx/src/library/schemamanager.cpp
  mixxx/src/library/setlogfeature.cpp
  mixxx/src/library/stardelegate.h
  mixxx/src/library/trackcollection.cpp
  mixxx/src/library/trackcollection.h
  mixxx/src/library/trackmodel.h
  mixxx/src/library/traktor/traktorfeature.cpp
  mixxx/src/library/traktor/traktorfeature.h
  mixxx/src/library/treeitem.cpp
  mixxx/src/library/treeitem.h
  mixxx/src/mixxx.cpp
  mixxx/src/mixxxkeyboard.cpp
  mixxx/src/mixxxkeyboard.h
  mixxx/src/playerinfo.cpp
  mixxx/src/playerinfo.h
  mixxx/src/recording/defs_recording.h
  mixxx/src/recording/encodermp3.cpp
  mixxx/src/recording/enginerecord.cpp
  mixxx/src/recording/enginerecord.h
  mixxx/src/recording/recordingmanager.cpp
  mixxx/src/recording/recordingmanager.h
  mixxx/src/skin/legacyskinparser.cpp
  mixxx/src/skin/legacyskinparser.h
  mixxx/src/soundmanager.cpp
  mixxx/src/soundmanager.h
  mixxx/src/track/beatgrid.cpp
  mixxx/src/track/beatgrid.h
  mixxx/src/track/beatmap.cpp
  mixxx/src/track/beatmap.h
  mixxx/src/track/beats.h
  mixxx/src/trackinfoobject.cpp
  mixxx/src/trackinfoobject.h
  mixxx/src/vamp/vampanalyser.cpp
  mixxx/src/waveform/renderers/glslwaveformrenderersignal.cpp
  mixxx/src/waveform/renderers/glslwaveformrenderersignal.h
  mixxx/src/waveform/renderers/glwaveformrendererfilteredsignal.cpp
  mixxx/src/waveform/renderers/glwaveformrendererfilteredsignal.h
  mixxx/src/waveform/renderers/glwaveformrenderersimplesignal.cpp
  mixxx/src/waveform/renderers/glwaveformrenderersimplesignal.h
  mixxx/src/waveform/renderers/waveformmark.cpp
  mixxx/src/waveform/renderers/waveformmark.h
  mixxx/src/waveform/renderers/waveformrenderbeat.cpp
  mixxx/src/waveform/renderers/waveformrenderbeat.h
  mixxx/src/waveform/renderers/waveformrendererendoftrack.cpp
  mixxx/src/waveform/renderers/waveformrendererfilteredsignal.cpp
  mixxx/src/waveform/renderers/waveformrendererfilteredsignal.h
  mixxx/src/waveform/renderers/waveformrendermark.cpp
  mixxx/src/waveform/renderers/waveformrendermark.h
  mixxx/src/waveform/renderers/waveformwidgetrenderer.cpp
  mixxx/src/waveform/renderers/waveformwidgetrenderer.h
  mixxx/src/waveform/waveform.cpp
  mixxx/src/waveform/waveform.h
  mixxx/src/waveform/waveformfactory.cpp
  mixxx/src/waveform/waveformfactory.h
  mixxx/src/waveform/waveformwidgetfactory.cpp
  mixxx/src/waveform/widgets/glsimplewaveformwidget.cpp
  mixxx/src/waveform/widgets/glsimplewaveformwidget.h
  mixxx/src/waveform/widgets/glslwaveformwidget.cpp
  mixxx/src/waveform/widgets/glslwaveformwidget.h
  mixxx/src/waveform/widgets/glwaveformwidget.cpp
  mixxx/src/waveform/widgets/softwarewaveformwidget.cpp
  mixxx/src/waveform/widgets/softwarewaveformwidget.h
  mixxx/src/waveform/widgets/waveformwidgettype.h
  mixxx/src/widget/wlibrarytableview.cpp
  mixxx/src/widget/woverview.cpp
  mixxx/src/widget/woverview.h
  mixxx/src/widget/wslidercomposed.h
  mixxx/src/widget/wspinny.cpp
  mixxx/src/widget/wstatuslight.cpp
  mixxx/src/widget/wtracktableview.cpp
  mixxx/src/widget/wtracktableview.h
  mixxx/src/widget/wwaveformviewer.cpp
  mixxx/vamp-plugins/SConscript
  mixxx/vamp-plugins/libmain.cpp
  mixxx/res/controllers/EKS Otus.cntrlr.xml
unknown:
  mixxx/.sconf_temp/
  mixxx/.sconsign.dblite
  mixxx/Mixxx/
  mixxx/cache/
  mixxx/config.log
  mixxx/lin32_build/
  mixxx/mixxx
  mixxx/output.txt
  mixxx/lib/vamp-2.3/src/vamp-sdk/PluginAdapter.os
  mixxx/lib/vamp-2.3/src/vamp-sdk/RealTime.os
  mixxx/res/qrc_mixxx.cc
pending merges:
  RJ Ryan 2012-05-24 [merge] Merging from lp:~mixxxdevelopers/mixxx/trunk_translations. Upda...
    Launchpad Translation... 2012-05-20 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-18 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-17 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-16 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-15 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-14 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-13 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-12 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-11 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-10 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-09 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-08 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-07 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-06 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-05 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-04 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-03 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-02 Launchpad automatic translations update.
    Launchpad Translation... 2012-05-01 Launchpad automatic translations update.
    Launchpad Translation... 2012-04-30 Launchpad automatic translations update.
    RJ Ryan 2012-05-24 Fix improper use of MultiByteToWideChar and WideCharToMultiByte in ...
    RJ Ryan 2012-05-24 Revert all debug output I added to debug the VAMP plugin on Windows...
    RJ Ryan 2012-05-24 Revert noisy plugin-path debug output.
    RJ Ryan 2012-05-24 Fix usage of MultiByteToWideChar in VAMP PluginLoader to include an...
    RJ Ryan 2012-05-23 Getting closer to a Windows VAMP plugin fix.
    RJ Ryan 2012-05-23 Getting closer to a Windows VAMP plugin fix.
    vrince 2012-05-23 [merge] Merge from trunk
    RJ Ryan 2012-05-23 Disable some debug console stuff I was trying.
    vrince 2012-05-23 Add WaveformMarkSet in order to generalize WaveformMark skin extract...
    RJ Ryan 2012-05-23 Disable DEBUGCONSOLE ifdef.
    RJ Ryan 2012-05-23 Add Max Linke's patch to put delegateForColumn in BSTM. Fixes Bug #...
    RJ Ryan 2012-05-22 Remove UNICODE definition. Write stdout/stderr/cerr/cout to file on...
    RJ Ryan 2012-05-22 Sigh. Adding excessive logging to PluginLoader. Will revert later.
    Daniel Schürmann 2012-05-22 check if track exists without cache, fix bug #974640
    RJ Ryan 2012-05-22 Try to fix Windows VAMP plugin loading by defining UNICODE.
    RJ Ryan 2012-05-22 Add windows debug output to VAMP plugin detection.
    RJ Ryan 2012-05-22 Use Int32_t instead of int32_t.
    RJ Ryan 2012-05-20 Fix BPM lock checkbox inconsistent behavior. Fixes Bug #1002051
    RJ Ryan 2012-05-20 Check BPM-lock for selection to decide whether to allow the clear-b...
    RJ Ryan 2012-05-20 Do not save waveforms immediately after generating them since the t...
    RJ Ryan 2012-05-20 Remove Qt::WA_ForceUpdatesDisabled from WWaveformViewer. Fixes Bug ...
    RJ Ryan 2012-05-20 Don't set StarDelegate as the delegate for every single cell. Inste...
    RJ Ryan 2012-05-20 [merge] Merging from lp:~hile/mixxx/hidscripts
    Ilkka Tuohela 2012-05-20 Imported latest fixes to hidscripts
    Ilkka Tuohela 2012-05-20 Imported latest fixes to hidscripts
    Ilkka Tuohela 2012-05-20 Imported latest fixes to hidscripts
    Ilkka Tuohela 2012-05-18 Minor fixes for LED control
    Ilkka Tuohela 2012-05-18 Imported latest big API changes
    Ilkka Tuohela 2012-05-18 Renamed field add functions, fix sparse bitVector index parsing
    Ilkka Tuohela 2012-05-18 Imported common-hid-packet-parser.js and latest Otus HID updates
    Daniel Schürmann 2012-05-20 fixed recording folder, Bug #925663
    RJ Ryan 2012-05-19 Update blacklisting logic and add comments.
    RJ Ryan 2012-05-19 [merge] Merging from lp:~hile/mixxx/hidblacklist.
    Ilkka Tuohela 2012-05-16 Imported HID blacklist patch proposal
    RJ Ryan 2012-05-19 Fix build. Mea culpa.
    RJ Ryan 2012-05-18 Fix widget names for translation.
    RJ Ryan 2012-05-18 [merge] Merging from lp:~vrince/mixxx/waveform-2.0_pure-gl
    vrince 2012-05-16 Change defaul zoom to 3 (33%)
    vrince 2012-05-16 Add DefaulMark support (see example in <visual> of Deere1280x800 skin)
    vrince 2012-05-16 Waveform interactive zoom in accessible everywhere in the waveform v...
    vrince 2012-05-15 Some comments
    vrince 2012-05-13 Add central waveform axe (and 'AxesColor' in skin) in all waveform r...
    vrince 2012-05-12 [merge] Merged from trunk
    vrince 2012-05-11 Disable end of track display for track shorter than end of track tri...
    vrince 2012-05-11 Add support for alignment in software signal renderer (and fix globa...
    vrince 2012-05-10 Use of visual gain in every waveform renderer (and add overall and p...
    vrince 2012-05-08 Update software signal renderer with lst signal base
    vrince 2012-05-08 Add QtSimpleWaveform and QtWaveformRendererSimpleSignal (move conten...
    vrince 2012-05-08 Add support for alignment in GLWaveformRendererFilteredSignal
    vrince 2012-05-08 Add new waveform widget QtWaveformWidget
    vrince 2012-05-08 Add WaveformRendererSignalBase to avoid duplicated code
    vrince 2012-05-07 [merge] Merge from trunk
    vrince 2012-05-07 Integrate test version with pure gl with triangles
    vrince 2012-05-03 introduce pure opengl signal render based on triangles (from old tes...
    Owen Williams 2012-05-16 Protect against NULL trackpointer when load fails
    Daniel Schürmann 2012-05-16 two more __OSX__->__APPLE__
    Daniel Schürmann 2012-05-16 __OSX__->__APPLE__, fix bug #999560
    Daniel Schürmann 2012-05-16 redo changes from #3157, which where lost in #3159
    Daniel Schürmann 2012-05-16 Add to Auto-DJ Top with empty Auto-DJ, fix bug #982652
    Daniel Schürmann 2012-05-15 fixed shortcuts for eject and LoadSelectedTrack, a partial...
    Daniel Schürmann 2012-05-15 [merge] fixed missing shortcuts in tooltips, bug #696058
    Daniel Schürmann 2012-05-15 fixed missing shortcuts in tooltips, bug #696058
    RJ Ryan 2012-05-15 Fix Windows build.
    RJ Ryan 2012-05-15 Set shoutcast stream public based on stream_public preference. Fixe...
    Daniel Schürmann 2012-05-15 fix *.kbd.cfg location
    RJ Ryan 2012-05-14 Forgot return value.
    RJ Ryan 2012-05-14 If QLocale::uiLanguages() has English as the first language then do...
    RJ Ryan 2012-05-14 [merge] Merge from lp:mixxx/1.10
    RJ Ryan 2012-05-14 Typo
    Daniel Schürmann 2012-05-14 additional keyboard layouts added
    Daniel Schürmann 2012-05-14 Load keyboard mappings based on actual keyboard layout, fi...
    RJ Ryan 2012-05-14 Delete AnalyserGainVamp and ReplayGain from libmixxxminimal.
    RJ Ryan 2012-05-14 Disable AnalyserGainVamp and use the built-in RG analyser for all a...
    RJ Ryan 2012-05-14 [merge] Merging from lp:mixxx/1.10.
    RJ Ryan 2012-05-14 Add vittorio's C++ and class-ified conversion of ReplayGain analyse...
    Daniel Schürmann 2012-05-14 removed absolute path when loading lame, fix bug #920191
    Daniel Schürmann 2012-05-14 hide smb songs from Rhythmbox library
    Bill Good 2012-05-13 Fixed SoundManager from filtering input devices with only 1 channel
    Bill Good 2012-05-13 Fixed DlgPrefSoundItem to only show devices with sufficient channels
    Bill Good 2012-05-13 Added unistd.h include for sleep(3) on non-Windows
    Daniel Schürmann 2012-05-14 removed absolute path when loading lame, fix bug #920191
    RJ Ryan 2012-05-14 Update translation templates.:
    RJ Ryan 2012-05-14 Fix spelling of coarse in MIDI learning.
    Daniel Schürmann 2012-05-14 hide smb songs from Rhythmbox library
    jus 2012-05-14 Update translation template
    Daniel Schürmann 2012-05-13 eject played track on end of Auto-DJ playlist, fix bug #99...
    RJ Ryan 2012-05-12 Rework ErrorDialogHandler to be a little cleaner and not Q_ASSERT.
    RJ Ryan 2012-05-12 [merge] Merging from lp:mixxx/1.10
    RJ Ryan 2012-05-12 Do not use display strings to store/communicate shoutcast preferenc...
    RJ Ryan 2012-05-11 Fix deadlock in BrowseThread. Fixes Bug #955179
    Daniel Schürmann 2012-05-12 fixed outline skin kill buttons
    Sean M. Pappalardo 2012-05-12 Prevent HIDAPI from bailing out on non-critical libusb e...
    RJ Ryan 2012-05-12 Add patch from mutil to allow customization of Browse section Quick...
    Owen Williams 2012-05-11 Make sure analyzers are cleaned up before we requeue a track
    RJ Ryan 2012-05-11 Fix deadlock in BrowseThread. Fixes Bug #955179
    RJ Ryan 2012-05-11 Add /usr/lib/mixxx to install directories in Debian packaging scripts.
    Owen Williams 2012-05-11 Make track analysis interruptable so loaded tracks always get...
    RJ Ryan 2012-05-11 Use SoundSource samplerate instead of TrackInfoObject samplerate in...
    RJ Ryan 2012-05-11 Add daschuer's patch to allow enabling/disabling of iTunes/Traktor/...
    RJ Ryan 2012-05-11 Add daschuer's patch to allow importing external-library playlists ...
    RJ Ryan 2012-05-10 Add Tom Gascoigne's patch to add a locale preference option, allowi...
    RJ Ryan 2012-05-10 Change WStatusLight behavior so that PathBack is not drawn on every...
    RJ Ryan 2012-05-10 Use a CO instead of COTM in SoundManager.
    Daniel Schürmann 2012-05-10 fix bug #900255 losing keyboard release events
    RJ Ryan 2012-05-10 Add path from mutil to show help text when clicking the main browse...
    RJ Ryan 2012-05-09 Set SoundManager status to disconnected if we fail to open a device...
    RJ Ryan 2012-05-09 Forgot to create CO for SoundManager.
    RJ Ryan 2012-05-09 Add patch from Maxime Bochon to suggest a filename based on the pla...
    RJ Ryan 2012-05-09 Add [SoundManager],status [Recording],status and [Shoutcast],status...
    jus <https://launchpa... 2012-05-09 Update translation template
    jus <https://launchpa... 2012-05-09 Small tooltips fixes (Typo, Double space, Capitali...
    RJ Ryan 2012-05-08 Remove Q_ASSERT on query failure and another Q_ASSERT on rollback.
    RJ Ryan 2012-05-08 Fix iTunes scanner so it doesn't run with the main thread database ...
    RJ Ryan 2012-05-08 Close DB connection on shutdown in AnalyserWaveform.
    RJ Ryan 2012-05-08 Add connectionName to debug output.
    RJ Ryan 2012-05-08 Don't clone the QSqlDatabase in AnalysisDao.
    RJ Ryan 2012-05-08 Convert all transactions to use ScopedTransaction so should never b...
    RJ Ryan 2012-05-08 Add missing rollback and delete unused SQL statement.
    RJ Ryan 2012-05-08 Fix typo.
    RJ Ryan 2012-05-08 Prioritize tracks that are loaded when choosing the next track to a...
    RJ Ryan 2012-05-08 Fix typo.
    RJ Ryan 2012-05-08 Update translation templates so LP translators can start work on to...
    RJ Ryan 2012-05-08 Wordsmith and de-duplicate tooltips.
    jus 2012-05-08 Adds a selectable Tracks File Path display in the now less high track e...
    RJ Ryan 2012-05-08 [merge] Merging from lp:~mixxxdevelopers/mixxx/1.11-skins
    jus 2012-05-08 [merge] Merge from trunk
    jus 2012-05-08 [merge] Merge from trunk
    jus 2012-05-08 Oops, fix copypasta error
    jus 2012-05-08 [merge] Merge from trunk
    jus 2012-05-08 Make "orientation" TooltipID more unambiguous
    jus 2012-05-08 Removed individual Tooltip text in all skin.xml. Tooltip now use a Tool...
    jus 2012-05-08 Add bpm_tap_visual_bpm TooltipId to tooltips.cpp . This is a special ca...
    jus 2012-05-08 Unify heading word capitalization for displayed tooltips in tooltips.cpp
    jus 2012-05-08 Add track_duration, track_album, text , TooltipId`s to tooltips.cpp
    jus 2012-05-08 Fix spelling in tooltips.cpp
    jus 2012-05-08 [merge] Merge from trunk
    jus 2012-05-06 Revert last commit, cause the crossfaders ALT+H shortcut is already tak...
    jus 2012-05-06 Added shortcuts to switch the crossfader from left to right channel in ...
    jus 2012-05-06 [merge] Merge from trunk
    jus 2012-05-05 Add tooltips for all buttons in dlgprepare.ui
    jus 2012-05-05 Add tooltips for ramping pitchbend in dlgprefcontrolsdlg.ui
    jus 2012-05-04 Align the call to action button to the right like in all other preferen...
    jus 2012-05-04 Unify wording, use "Reset to Defaults" accross all preferences panes ( ...
    jus 2012-05-04 Clean up Preferences->Interface widget, fix lp:992144
    jus 2012-05-04 [merge] Merge from trunk
    jus 2012-05-04 Apply styles to spinbox in AutoDJ tab in all skin.xml
    jus 2012-05-04 Made QSpinbox in dlgautodj.ui frameless and remove its fixed size. Need...
    jus 2012-05-04 Added QSpinbox to the existing workaround to support legacy color styli...
    jus 2012-05-04 Fix mini tooltips typo in all skin.xml
    jus 2012-05-03 [merge] Merge from trunk
    jus 2012-05-03 Fix typo in recordings preferences
    jus 2012-05-03 Update icons for the renamed History feature (was Set Log)
    jus 2012-05-02 [merge] Merge from trunk
    jus 2012-05-01 Update tooltips & use of EmitOnPressAndRelease for bpm_tab in Phoney(Da...
    jus 2012-05-01 Fix spacing in some skin.xml
    jus 2012-05-01 Added element missed in the previous copypasta fest in Deere1440x900-WX...
    jus 2012-05-01 Update tooltips & use of EmitOnPressAndRelease for bpm_tab with Outline...
    jus 2012-05-01 Bump target Mixxx version to 1.11 in skin.xml & CHANGELOG.txt
    jus 2012-05-01 Deere skin family: Move changelog from skin.xml to separate CHANGELOG.t...
    jus 2012-05-01 Shade skin family: Move changelog from skin.xml to separate CHANGELOG.t...
    jus 2012-05-01 PhoneyDark skin family: Move changelog from skin.xml to separate CHANGE...
    jus 2012-05-01 Phoney skin family: Move changelog from skin.xml to separate CHANGELOG....
    jus 2012-05-01 Outline skin family: Move changelog from skin.xml to separate CHANGELOG...
    jus 2012-05-01 LateNightBlues skin family: Move changelog from skin.xml to separate CH...
    jus 2012-05-01 LateNight skin family: Move changelog from skin.xml to separate CHANGEL...
    jus 2012-05-01 Deere skin family: Move changelog from skin.xml to separate CHANGELOG.t...
    jus 2012-05-01 Support <Mark> (Hot)Cue and <MarkRange> Loop marker in waveform <Overvi...
    RJ Ryan 2012-05-08 Add patch from mutil to add right-click 'Open in file browser' cont...
    RJ Ryan 2012-05-08 Don't short-circuit Analyser::initialise calls.
    RJ Ryan 2012-05-08 Revert r3103.
    Sean M. Pappalardo 2012-05-08 Write beat detection defaults to config file on startup ...
    Sean M. Pappalardo 2012-05-08 Fixed waveforms again after r3101
    RJ Ryan 2012-05-08 Add waveform analyser to prepare-analyser-queue so that waveforms w...
    RJ Ryan 2012-05-08 Don't run the analyser-queue on tracks for which no analyser wants ...
    RJ Ryan 2012-05-08 [merge] Merging from lp:mixxx/1.10
    RJ Ryan 2012-05-08 Fix integer overflow when using 4GB recording splits. Fixes Bug #95...
    RJ Ryan 2012-05-07 Fix deletion order in DlgAutoDJ to fix header-state saving for the ...
    RJ Ryan 2012-05-07 Move tooltips out of skins and into src/skin/tooltips.cpp. Add Tool...
    RJ Ryan 2012-05-07 Re-work Beats::findBeats to not append to a QList<double>. Instead,...
    Owen Williams 2012-05-06 Add vinylcontrol_enabled to midi learning (lp:#995658)
    RJ Ryan 2012-05-06 [merge] Merging from lp:mixxx/1.10
    RJ Ryan 2012-05-06 Remove Q_ASSERT on angle in WSpinny and gracefully handle the error...

3014. By globalkeith

merge with trunk

3013. By globalkeith

update from trunk

3012. By globalkeith

extensive refactoring to clean up code and added a row count UI so you can see how many tracks are matched by the filters

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'mixxx/build/depends.py'
2--- mixxx/build/depends.py 2012-06-25 00:02:38 +0000
3+++ mixxx/build/depends.py 2012-06-27 11:06:21 +0000
4@@ -324,6 +324,7 @@
5 "dlgprefcrossfader.cpp",
6 "dlgtrackinfo.cpp",
7 "dlgprepare.cpp",
8+ "dlgselector.cpp",
9 "dlgautodj.cpp",
10
11 "engine/engineworker.cpp",
12@@ -424,6 +425,7 @@
13 "widget/wlibrary.cpp",
14 "widget/wlibrarytableview.cpp",
15 "widget/wpreparelibrarytableview.cpp",
16+ "widget/wselectorlibrarytableview.cpp",
17 "widget/wpreparecratestableview.cpp",
18 "widget/wlibrarytextbrowser.cpp",
19 "library/preparecratedelegate.cpp",
20@@ -455,6 +457,9 @@
21 "dlgrecording.cpp",
22 "recording/recordingmanager.cpp",
23
24+ "library/selector/selectorfeature.cpp",
25+ "library/selector/selectorlibrarytablemodel.cpp",
26+
27 # External Library Features
28 "library/baseexternallibraryfeature.cpp",
29 "library/rhythmbox/rhythmboxfeature.cpp",
30@@ -629,6 +634,7 @@
31 build.env.Uic4('dlgaboutdlg.ui')
32 build.env.Uic4('dlgtrackinfo.ui')
33 build.env.Uic4('dlgprepare.ui')
34+ build.env.Uic4('dlgselector.ui')
35 build.env.Uic4('dlgautodj.ui')
36 build.env.Uic4('dlgprefsounditem.ui')
37 build.env.Uic4('dlgrecording.ui')
38
39=== added file 'mixxx/lib/replaygain/replaygain_analysis.h'
40--- mixxx/lib/replaygain/replaygain_analysis.h 1970-01-01 00:00:00 +0000
41+++ mixxx/lib/replaygain/replaygain_analysis.h 2012-06-27 11:06:21 +0000
42@@ -0,0 +1,58 @@
43+/*
44+ * ReplayGainAnalysis - analyzes input samples and give the recommended dB change
45+ * Copyright (C) 2001 David Robinson and Glen Sawyer
46+ *
47+ * This library is free software; you can redistribute it and/or
48+ * modify it under the terms of the GNU Lesser General Public
49+ * License as published by the Free Software Foundation; either
50+ * version 2.1 of the License, or (at your option) any later version.
51+ *
52+ * This library is distributed in the hope that it will be useful,
53+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
54+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
55+ * Lesser General Public License for more details.
56+ *
57+ * You should have received a copy of the GNU Lesser General Public
58+ * License along with this library; if not, write to the Free Software
59+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
60+ *
61+ * concept and filter values by David Robinson (David@Robinson.org)
62+ * -- blame him if you think the idea is flawed
63+ * coding by Glen Sawyer (glensawyer@hotmail.com) 442 N 700 E, Provo, UT 84606 USA
64+ * -- blame him if you think this runs too slowly, or the coding is otherwise flawed
65+ * minor cosmetic tweaks to integrate with FLAC by Josh Coalson
66+ *
67+ * For an explanation of the concepts and the basic algorithms involved, go to:
68+ * http://www.replaygain.org/
69+ */
70+
71+#ifndef GAIN_ANALYSIS_H
72+#define GAIN_ANALYSIS_H
73+
74+#include <stddef.h>
75+
76+#define GAIN_NOT_ENOUGH_SAMPLES -24601
77+#define GAIN_ANALYSIS_ERROR 0
78+#define GAIN_ANALYSIS_OK 1
79+#define INIT_GAIN_ANALYSIS_ERROR 0
80+#define INIT_GAIN_ANALYSIS_OK 1
81+
82+#ifdef __cplusplus
83+extern "C" {
84+#endif
85+
86+typedef float Float_t; /* Type used for filtering */
87+
88+extern Float_t ReplayGainReferenceLoudness; /* in dB SPL, currently == 89.0 */
89+
90+int InitGainAnalysis ( long samplefreq );
91+int AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels );
92+int ResetSampleFrequency ( long samplefreq );
93+Float_t GetTitleGain ( void );
94+/*Float_t GetAlbumGain ( void );*/
95+
96+#ifdef __cplusplus
97+}
98+#endif
99+
100+#endif /* GAIN_ANALYSIS_H */
101
102=== added file 'mixxx/res/images/library/ic_library_selector.png'
103Binary files mixxx/res/images/library/ic_library_selector.png 1970-01-01 00:00:00 +0000 and mixxx/res/images/library/ic_library_selector.png 2012-06-27 11:06:21 +0000 differ
104=== added file 'mixxx/res/keyboard/Standard.kbd.cfg'
105--- mixxx/res/keyboard/Standard.kbd.cfg 1970-01-01 00:00:00 +0000
106+++ mixxx/res/keyboard/Standard.kbd.cfg 2012-06-27 11:06:21 +0000
107@@ -0,0 +1,101 @@
108+[Master]
109+crossfader_up h
110+crossfader_down g
111+crossfader_up_small Shift+h
112+crossfader_down_small Shift+g
113+
114+[Microphone]
115+talkover `
116+
117+[Channel1]
118+play d
119+cue_set Shift+d
120+cue_default f
121+cue_gotoandstop Shift+f
122+
123+back a
124+reverse Shift+a
125+fwd s
126+
127+beatsync 1
128+bpm_tap Shift+!
129+
130+rate_perm_down F1
131+rate_perm_up F2
132+rate_temp_down F3
133+rate_temp_up F4
134+rate_perm_down_small Shift+F1
135+rate_perm_up_small Shift+F2
136+rate_temp_down_small Shift+F3
137+rate_temp_up_small Shift+F4
138+
139+pfl t
140+filterLowKill b
141+flanger 5
142+
143+hotcue_1_activate z
144+hotcue_2_activate x
145+hotcue_3_activate c
146+hotcue_4_activate v
147+hotcue_1_clear Shift+z
148+hotcue_2_clear Shift+x
149+hotcue_3_clear Shift+c
150+hotcue_4_clear Shift+v
151+
152+LoadSelectedTrack Shift+Left
153+eject Alt+Shift+Left
154+
155+loop_in 2
156+loop_out 3
157+reloop_exit 4
158+
159+beatloop_4_toggle q
160+loop_halve w
161+loop_double e
162+
163+
164+[Channel2]
165+play l
166+cue_set Shift+l
167+cue_default ;
168+cue_gotoandstop Shift+:
169+
170+back j
171+reverse Shift+j
172+fwd k
173+
174+beatsync 6
175+bpm_tap Shift+^
176+
177+rate_perm_down F5
178+rate_perm_up F6
179+rate_temp_down F7
180+rate_temp_up F8
181+rate_perm_down_small Shift+F5
182+rate_perm_up_small Shift+F6
183+rate_temp_down_small Shift+F7
184+rate_temp_up_small Shift+F8
185+
186+pfl y
187+filterLowKill n
188+flanger 0
189+
190+hotcue_1_activate m
191+hotcue_2_activate ,
192+hotcue_3_activate .
193+hotcue_4_activate /
194+hotcue_1_clear Shift+M
195+hotcue_2_clear Shift+<
196+hotcue_3_clear Shift+>
197+hotcue_4_clear Shift+?
198+
199+LoadSelectedTrack Shift+Right
200+eject Alt+Shift+Right
201+
202+loop_in 7
203+loop_out 8
204+reloop_exit 9
205+
206+beatloop_4_toggle u
207+loop_halve i
208+loop_double o
209
210=== modified file 'mixxx/res/keyboard/en_US.kbd.cfg'
211--- mixxx/res/keyboard/en_US.kbd.cfg 2012-05-14 21:39:03 +0000
212+++ mixxx/res/keyboard/en_US.kbd.cfg 2012-06-27 11:06:21 +0000
213@@ -1,8 +1,8 @@
214 [Master]
215 crossfader_up h
216 crossfader_down g
217-crossfader_up_small Shift+h
218-crossfader_down_small Shift+g
219+crossfader_maximum Shift+h
220+crossfader_minimum Shift+g
221
222 [Microphone]
223 talkover `
224
225=== modified file 'mixxx/res/mixxx.qrc'
226--- mixxx/res/mixxx.qrc 2012-06-25 00:02:38 +0000
227+++ mixxx/res/mixxx.qrc 2012-06-27 11:06:21 +0000
228@@ -30,6 +30,7 @@
229 <file>images/library/ic_library_locked.png</file>
230 <file>images/library/ic_library_playlist.png</file>
231 <file>images/library/ic_library_prepare.png</file>
232+ <file>images/library/ic_library_selector.png</file>
233 <file>images/library/ic_library_promotracks.png</file>
234 <file>images/library/ic_library_recordings.png</file>
235 <file>images/library/ic_library_rhythmbox.png</file>
236
237=== added file 'mixxx/src/analyserwavesummary.cpp'
238--- mixxx/src/analyserwavesummary.cpp 1970-01-01 00:00:00 +0000
239+++ mixxx/src/analyserwavesummary.cpp 2012-06-27 11:06:21 +0000
240@@ -0,0 +1,81 @@
241+#include <QtDebug>
242+
243+#include "trackinfoobject.h"
244+#include "analyserwavesummary.h"
245+
246+// Taken from the old wavesummary.cpp code
247+#ifndef WAVESUMMARYCONSTANTS
248+const int kiBlockSize = 2048;
249+const int kiBeatBlockNo = 1000;
250+const int kiBeatBins = 100;
251+const int kiSummaryBufferSize = 2100;
252+const float kfFeatureStepSize = 0.01f;
253+#define WAVESUMMARYCONSTANTS
254+#endif
255+
256+AnalyserWavesummary::AnalyserWavesummary() {
257+ m_pData = NULL;
258+}
259+
260+void AnalyserWavesummary::initialise(TrackPointer tio, int sampleRate, int totalSamples) {
261+ Q_UNUSED(sampleRate);
262+ // Check if the preview has already been generated
263+ const QByteArray* p = tio->getWaveSummary();
264+ if(p != NULL && p->size() > 0) {
265+ return;
266+ }
267+
268+ // Initialize kiSummaryBufferSize bytes to 0
269+ m_pData = new QByteArray(kiSummaryBufferSize, 0);
270+
271+ // The stride length is the number of samples that correspond
272+ // to one "line" (3 entries) in the data buffer.
273+ m_iStrideLength = (int)ceilf((float)totalSamples/((float)kiSummaryBufferSize/3.));
274+ if(m_iStrideLength%2 != 0)
275+ m_iStrideLength--;
276+
277+ m_iCurPos = 0;
278+ m_iBufferPos = 0;
279+ m_fMax = -1.0f;
280+ m_fMin = 1.0f;
281+}
282+
283+void AnalyserWavesummary::process(const CSAMPLE *pIn, const int iLen) {
284+ // Check if processing is disabled.
285+ if(m_pData == NULL)
286+ return;
287+
288+ //qDebug() << "AnalyserWavesummary::process() processing " << iLen << " samples";
289+
290+ for(int i=0; i<iLen && m_iCurPos+2 < m_pData->size() ; i++) {
291+ if(m_iBufferPos >= m_iStrideLength) {
292+ (*m_pData)[m_iCurPos] = (char)(m_fMin*127);
293+ (*m_pData)[m_iCurPos+1] = (char)(m_fMax*127);
294+ (*m_pData)[m_iCurPos+2] = 0;
295+ m_iCurPos += 3;
296+
297+ m_iBufferPos = 0;
298+ m_fMax = -1.0f;
299+ m_fMin = 1.0f;
300+ }
301+
302+ if(m_iBufferPos <= kiBlockSize) {
303+ if(pIn[i] > m_fMax)
304+ m_fMax = pIn[i];
305+ if(pIn[i] < m_fMin)
306+ m_fMin = pIn[i];
307+ }
308+ m_iBufferPos++;
309+ }
310+}
311+
312+void AnalyserWavesummary::finalise(TrackPointer tio) {
313+ if(m_pData == NULL)
314+ return;
315+ tio->setWaveSummary(m_pData, true);
316+ //setWaveSummary copies the waveform from the pointer we pass it, so it's safe
317+ //to delete the pointer.
318+ delete m_pData;
319+ m_pData = NULL;
320+ //qDebug() << "AnalyserWavesummary generation successful for " << tio->getFilename();
321+}
322
323=== added file 'mixxx/src/analyserwavesummary.h'
324--- mixxx/src/analyserwavesummary.h 1970-01-01 00:00:00 +0000
325+++ mixxx/src/analyserwavesummary.h 2012-06-27 11:06:21 +0000
326@@ -0,0 +1,25 @@
327+#ifndef ANALYSER_WAVESUMMARY_H
328+#define ANALYSER_WAVESUMMARY_H
329+
330+#include <QByteArray>
331+
332+#include "analyser.h"
333+
334+class AnalyserWavesummary : public Analyser {
335+
336+ public:
337+ AnalyserWavesummary();
338+ void initialise(TrackPointer tio, int sampleRate, int totalSamples);
339+ void process(const CSAMPLE *pIn, const int iLen);
340+ void finalise(TrackPointer tio);
341+
342+ private:
343+ QByteArray *m_pData;
344+ int m_iCurPos;
345+ int m_iBufferPos;
346+ int m_iStrideLength;
347+ float m_fMax;
348+ float m_fMin;
349+};
350+
351+#endif
352
353=== modified file 'mixxx/src/dlgautodj.cpp'
354--- mixxx/src/dlgautodj.cpp 2012-06-25 00:02:38 +0000
355+++ mixxx/src/dlgautodj.cpp 2012-06-27 11:06:21 +0000
356@@ -129,6 +129,9 @@
357 m_pAutoDJTableModel->select();
358 }
359
360+void DlgAutoDJ::onHide() {
361+}
362+
363 void DlgAutoDJ::setup(QDomNode node) {
364 QPalette pal = palette();
365
366
367=== modified file 'mixxx/src/dlgautodj.h'
368--- mixxx/src/dlgautodj.h 2012-06-25 00:02:38 +0000
369+++ mixxx/src/dlgautodj.h 2012-06-27 11:06:21 +0000
370@@ -28,6 +28,7 @@
371 virtual void onSearchCleared();
372 virtual void onSearch(const QString& text);
373 virtual void onShow();
374+ virtual void onHide();
375 virtual void loadSelectedTrack();
376 virtual void loadSelectedTrackToGroup(QString group);
377 virtual void moveSelection(int delta);
378
379=== added file 'mixxx/src/dlgmidilearning.cpp'
380--- mixxx/src/dlgmidilearning.cpp 1970-01-01 00:00:00 +0000
381+++ mixxx/src/dlgmidilearning.cpp 2012-06-27 11:06:21 +0000
382@@ -0,0 +1,210 @@
383+/***************************************************************************
384+ dlgmidilearning.cpp - description
385+ -------------------
386+ begin : Mon March 2 2009
387+ copyright : (C) 2009 by Albert Santoni
388+ email : alberts at mixxx dot org
389+ ***************************************************************************/
390+
391+/***************************************************************************
392+* *
393+* This program is free software; you can redistribute it and/or modify *
394+* it under the terms of the GNU General Public License as published by *
395+* the Free Software Foundation; either version 2 of the License, or *
396+* (at your option) any later version. *
397+* *
398+***************************************************************************/
399+
400+#include "dlgmidilearning.h"
401+#include "mixxxcontrol.h"
402+#include "midi/midimessage.h"
403+#include "midi/midimapping.h"
404+
405+DlgMidiLearning::DlgMidiLearning(QWidget * parent, MidiMapping* mapping) : QDialog(parent), Ui::DlgMidiLearning()
406+{
407+ setupUi(this);
408+ m_pMidiMapping = mapping;
409+ labelMappedTo->setText("");
410+ iCurrentControl = 0;
411+ stackedWidget->setCurrentIndex(0); //Ensure the first page is always shown regardless
412+ //of the last page shown when the .ui file was saved.
413+
414+ //Delete this dialog when its closed. We don't want any persistence.
415+ QWidget::setAttribute(Qt::WA_DeleteOnClose);
416+
417+ m_pSkipShortcut = new QShortcut(QKeySequence(Qt::Key_Space), this);
418+ connect(m_pSkipShortcut, SIGNAL(triggered()), pushButtonSkip, SLOT(click()));
419+ //pushButtonSkip->setShortcut(QKeySequence(Qt::Key_Space));
420+
421+ connect(pushButtonBegin, SIGNAL(clicked()), this, SLOT(begin()));
422+ connect(pushButtonSkip, SIGNAL(clicked()), this, SLOT(next()));
423+ connect(pushButtonPrevious, SIGNAL(clicked()), this, SLOT(prev()));
424+ connect(m_pMidiMapping, SIGNAL(midiLearningFinished(MidiMessage)), this, SLOT(controlMapped(MidiMessage)));
425+ connect(pushButtonFinish, SIGNAL(clicked()), this, SLOT(close()));
426+
427+
428+ // Master Controls
429+ setupControl("[Master]", "crossfader", tr("Crossfader"));
430+ setupControl("[Master]", "volume", tr("Master volume"));
431+ setupControl("[Master]", "balance", tr("Master balance"));
432+ setupControl("[Master]", "headVolume", tr("Headphones volume"));
433+ setupControl("[Master]", "headMix", tr("Headphones mix (pre/main)"));
434+
435+ // Deck Controls
436+ setupDeckControl("cue_default", tr("Cue button for Player %1"));
437+ setupDeckControl("play", tr("Play button for Player %1"));
438+ setupDeckControl("back", tr("Fast rewind button for Player %1"));
439+ setupDeckControl("fwd", tr("Fast forward button for Player %1"));
440+ setupDeckControl("reverse", tr("Play reverse button for Player %1"));
441+ setupDeckControl("pfl", tr("Headphone listen button for Player %1"));
442+ setupDeckControl("beatsync", tr("Beat sync button for Player %1"));
443+ setupDeckControl("bpm", tr("BPM tap button for Player %1"));
444+ setupDeckControl("keylock", tr("Keylock button for Player %1"));
445+ setupDeckControl("rate", tr("Pitch control slider for Player %1"));
446+ setupDeckControl("flanger", tr("Flanger effect button for Player %1"));
447+ setupDeckControl("volume", tr("Channel %1 volume fader"));
448+ setupDeckControl("pregain", tr("Gain knob for Channel %1"));
449+ setupDeckControl("filterHigh", tr("High EQ knob for Channel %1"));
450+ setupDeckControl("filterMid", tr("Mid EQ knob for Channel %1"));
451+ setupDeckControl("filterLow", tr("Low EQ knob for Channel %1"));
452+ setupDeckControl("loop_in", tr("Loop In button for Player %1"));
453+ setupDeckControl("loop_out", tr("Loop Out button for Player %1"));
454+ setupDeckControl("reloop_exit", tr("Reloop / Exit button for Player %1"));
455+ setupDeckControl("beatloop_4", tr("Setup a loop over 4 beats for Player %1"));
456+ setupDeckControl("loop_halve", tr("Halves the current loop's length for Player %1"));
457+ setupDeckControl("loop_double", tr("Doubles the current loop's length for Player %1"));
458+ setupDeckControl("hotcue_1_activate", tr("Hotcue 1 button for Player %1"));
459+ setupDeckControl("hotcue_2_activate", tr("Hotcue 2 button for Player %1"));
460+ setupDeckControl("hotcue_3_activate", tr("Hotcue 3 button for Player %1"));
461+ setupDeckControl("hotcue_4_activate", tr("Hotcue 4 button for Player %1"));
462+ setupDeckControl("hotcue_1_clear", tr("Hotcue 1 delete button for Player %1"));
463+ setupDeckControl("hotcue_2_clear", tr("Hotcue 2 delete button for Player %1"));
464+ setupDeckControl("hotcue_3_clear", tr("Hotcue 3 delete button for Player %1"));
465+ setupDeckControl("hotcue_4_clear", tr("Hotcue 4 delete button for Player %1"));
466+
467+ setupSamplerControl("play", tr("Play button for Sampler %1"));
468+ setupSamplerControl("pregain", tr("Gain knob for Sampler %1"));
469+ setupSamplerControl("pfl", tr("Headphone listen button for Sampler %1"));
470+ setupSamplerControl("bpm", tr("BPM tap button for Sampler %1"));
471+ setupSamplerControl("keylock", tr("Keylock button for Sampler %1"));
472+ setupSamplerControl("rate", tr("Pitch control slider for Sampler %1"));
473+ setupSamplerControl("hotcue_1_activate", tr("Hotcue 1 button for Sampler %1"));
474+ setupSamplerControl("hotcue_2_activate", tr("Hotcue 2 button for Sampler %1"));
475+ setupSamplerControl("hotcue_3_activate", tr("Hotcue 3 button for Sampler %1"));
476+ setupSamplerControl("hotcue_4_activate", tr("Hotcue 4 button for Sampler %1"));
477+ setupSamplerControl("hotcue_1_clear", tr("Hotcue 1 delete button for Sampler %1"));
478+ setupSamplerControl("hotcue_2_clear", tr("Hotcue 2 delete button for Sampler %1"));
479+ setupSamplerControl("hotcue_3_clear", tr("Hotcue 3 delete button for Sampler %1"));
480+ setupSamplerControl("hotcue_4_clear", tr("Hotcue 4 delete button for Sampler %1"));
481+
482+ // Library Controls
483+ setupControl("[Playlist]", "SelectNextPlaylist", tr("Switch to the next view (library, playlist..)"));
484+ setupControl("[Playlist]", "SelectPrevPlaylist", tr("Switch to the previous view (library, playlist..)"));
485+ setupControl("[Playlist]", "SelectNextTrack", tr("Scroll to next track in library/playlist"));
486+ setupControl("[Playlist]", "SelectPrevTrack", tr("Scroll to previous track in library/playlist"));
487+ setupControl("[Playlist]", "LoadSelectedIntoFirstStopped", tr("Load selected track into first stopped player"));
488+ setupDeckControl("LoadSelectedTrack", tr("Load selected track into Player %1"));
489+
490+ // Flanger Controls
491+ setupControl("[Flanger]", "lfoPeriod", tr("Adjusts the wavelength of the flange effect"));
492+ setupControl("[Flanger]", "lfoDepth", tr("Adjusts the intensity of the flange effect"));
493+ setupControl("[Flanger]", "lfoDelay", tr("Adjusts the phase delay of the flange effect"));
494+
495+ // Microphone Controls
496+ setupControl("[Microphone]", "talkover", tr("Microphone on/off"));
497+ setupControl("[Microphone]", "volume", tr("Microphone volume"));
498+}
499+
500+void DlgMidiLearning::setupControl(QString group, QString control, QString description) {
501+ m_controlsToBind.append(qMakePair(MixxxControl(group, control), description));
502+}
503+
504+void DlgMidiLearning::setupDeckControl(QString control, QString description) {
505+ // TODO(rryan) get this from the PlayerManager
506+ const int iNumDecks = 2;
507+ for (int i = 1; i <= iNumDecks; ++i) {
508+ QString group = QString("[Channel%1]").arg(i);
509+ m_controlsToBind.append(qMakePair(MixxxControl(group, control), description.arg(i)));
510+ }
511+}
512+
513+void DlgMidiLearning::setupSamplerControl(QString control, QString description) {
514+ // TODO(rryan) get this from the PlayerManager
515+ const int iNumSamplers = 4;
516+ for (int i = 1; i <= iNumSamplers; ++i) {
517+ QString group = QString("[Sampler%1]").arg(i);
518+ m_controlsToBind.append(qMakePair(MixxxControl(group, control), description.arg(i)));
519+ }
520+}
521+
522+DlgMidiLearning::~DlgMidiLearning()
523+{
524+ //If there was any ongoing learning, cancel it (benign if there wasn't).
525+ m_pMidiMapping->cancelMidiLearn();
526+
527+ delete m_pSkipShortcut;
528+}
529+
530+void DlgMidiLearning::begin()
531+{
532+ //Switch pages in the stacked widget so that we show the mapping stuff.
533+ stackedWidget->setCurrentIndex(1);
534+
535+ //Tell the MIDI mapping to start learning the first control.
536+ iCurrentControl = 0;
537+ QPair<MixxxControl, QString>& control = m_controlsToBind[iCurrentControl];
538+ m_pMidiMapping->beginMidiLearn(control.first);
539+ labelMixxxControl->setText(control.second);
540+}
541+
542+void DlgMidiLearning::next()
543+{
544+ iCurrentControl++;
545+ if (iCurrentControl < m_controlsToBind.size())
546+ {
547+
548+ QPair<MixxxControl, QString>& control = m_controlsToBind[iCurrentControl];
549+ m_pMidiMapping->beginMidiLearn(control.first);
550+ labelMixxxControl->setText(control.second);
551+ pushButtonSkip->setText(tr("Skip"));
552+
553+ labelMappedTo->setText("");
554+ pushButtonPrevious->setEnabled(true);
555+ }
556+ else
557+ {
558+ //We've hit the end, show the congrats pane
559+ stackedWidget->setCurrentIndex(2);
560+ }
561+}
562+
563+void DlgMidiLearning::prev()
564+{
565+ iCurrentControl--;
566+ if (iCurrentControl >= 0)
567+ {
568+ QPair<MixxxControl, QString>& control = m_controlsToBind[iCurrentControl];
569+ m_pMidiMapping->beginMidiLearn(control.first);
570+ labelMixxxControl->setText(control.second);
571+ pushButtonSkip->setText(tr("Skip"));
572+ labelMappedTo->setText("");
573+
574+ //We've hit the start, don't let the user go back anymore.
575+ if (iCurrentControl == 0)
576+ pushButtonPrevious->setEnabled(false);
577+ }
578+
579+}
580+
581+/** Gets called when a control has just been mapped successfully */
582+void DlgMidiLearning::controlMapped(MidiMessage message)
583+{
584+ //QThread::sleep(1);
585+
586+ labelMappedTo->setText(tr("Successfully mapped to: ") + message.toString());
587+
588+ //Set the label on the "Skip" button to "Next" because we're proceeding
589+ //instead of "skipping" now. Makes more sense, I think...
590+ pushButtonSkip->setText(tr("Next"));
591+ //next();
592+}
593
594=== added file 'mixxx/src/dlgmidilearning.h'
595--- mixxx/src/dlgmidilearning.h 1970-01-01 00:00:00 +0000
596+++ mixxx/src/dlgmidilearning.h 2012-06-27 11:06:21 +0000
597@@ -0,0 +1,56 @@
598+/***************************************************************************
599+ dlgmidilearning.cpp - description
600+ -------------------
601+ begin : Mon March 2 2009
602+ copyright : (C) 2009 by Albert Santoni
603+ email : alberts at mixxx dot org
604+ ***************************************************************************/
605+
606+/***************************************************************************
607+ * *
608+ * This program is free software; you can redistribute it and/or modify *
609+ * it under the terms of the GNU General Public License as published by *
610+ * the Free Software Foundation; either version 2 of the License, or *
611+ * (at your option) any later version. *
612+ * *
613+ ***************************************************************************/
614+
615+#ifndef DLGMIDILEARNING_H
616+#define DLGMIDILEARNING_H
617+
618+#include <QtCore>
619+#include <QtGui>
620+#include <QPair>
621+
622+#include "ui_dlgmidilearning.h"
623+#include "configobject.h"
624+#include "mixxxcontrol.h"
625+#include "midi/midimessage.h"
626+
627+class MidiMapping;
628+
629+/**
630+ *@author Albert Santoni
631+ */
632+
633+class DlgMidiLearning : public QDialog, public Ui::DlgMidiLearning {
634+ Q_OBJECT
635+ public:
636+ DlgMidiLearning(QWidget *parent, MidiMapping* mapping);
637+ ~DlgMidiLearning();
638+ public slots:
639+ void begin(); /** Begin the MIDI learning phase */
640+ void next(); /** Ask to map the next control */
641+ void prev(); /** Ask to map the previous control */
642+ void controlMapped(MidiMessage); /** Gets called when a control has just been mapped successfully */
643+ private:
644+ void setupControl(QString group, QString control, QString helpText);
645+ void setupDeckControl(QString control, QString helpText);
646+ void setupSamplerControl(QString control, QString helpText);
647+ MidiMapping* m_pMidiMapping;
648+ QList<QPair<MixxxControl, QString> > m_controlsToBind;
649+ int iCurrentControl; /** Used to iterate through the controls list */
650+ QShortcut* m_pSkipShortcut;
651+};
652+
653+#endif
654
655=== added file 'mixxx/src/dlgmidilearning.ui'
656--- mixxx/src/dlgmidilearning.ui 1970-01-01 00:00:00 +0000
657+++ mixxx/src/dlgmidilearning.ui 2012-06-27 11:06:21 +0000
658@@ -0,0 +1,241 @@
659+<ui version="4.0" >
660+ <class>DlgMidiLearning</class>
661+ <widget class="QDialog" name="DlgMidiLearning" >
662+ <property name="geometry" >
663+ <rect>
664+ <x>0</x>
665+ <y>0</y>
666+ <width>366</width>
667+ <height>271</height>
668+ </rect>
669+ </property>
670+ <property name="windowTitle" >
671+ <string>MIDI Learning Wizard</string>
672+ </property>
673+ <layout class="QGridLayout" name="gridLayout" >
674+ <item row="0" column="0" >
675+ <widget class="QStackedWidget" name="stackedWidget" >
676+ <property name="currentIndex" >
677+ <number>2</number>
678+ </property>
679+ <widget class="QWidget" name="page" >
680+ <layout class="QGridLayout" name="gridLayout_2" >
681+ <item row="0" column="0" colspan="3" >
682+ <widget class="QLabel" name="label_2" >
683+ <property name="font" >
684+ <font>
685+ <weight>75</weight>
686+ <bold>true</bold>
687+ </font>
688+ </property>
689+ <property name="text" >
690+ <string>Welcome to the MIDI Learning Wizard</string>
691+ </property>
692+ <property name="alignment" >
693+ <set>Qt::AlignCenter</set>
694+ </property>
695+ </widget>
696+ </item>
697+ <item row="1" column="0" colspan="3" >
698+ <widget class="QLabel" name="label_3" >
699+ <property name="text" >
700+ <string>This wizard allows you to easily map the controls on your MIDI controller to Mixxx's controls.</string>
701+ </property>
702+ <property name="wordWrap" >
703+ <bool>true</bool>
704+ </property>
705+ </widget>
706+ </item>
707+ <item row="2" column="0" >
708+ <spacer name="horizontalSpacer" >
709+ <property name="orientation" >
710+ <enum>Qt::Horizontal</enum>
711+ </property>
712+ <property name="sizeHint" stdset="0" >
713+ <size>
714+ <width>116</width>
715+ <height>20</height>
716+ </size>
717+ </property>
718+ </spacer>
719+ </item>
720+ <item row="2" column="1" >
721+ <widget class="QPushButton" name="pushButtonBegin" >
722+ <property name="sizePolicy" >
723+ <sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
724+ <horstretch>0</horstretch>
725+ <verstretch>0</verstretch>
726+ </sizepolicy>
727+ </property>
728+ <property name="text" >
729+ <string>Begin</string>
730+ </property>
731+ </widget>
732+ </item>
733+ <item row="2" column="2" >
734+ <spacer name="horizontalSpacer_2" >
735+ <property name="orientation" >
736+ <enum>Qt::Horizontal</enum>
737+ </property>
738+ <property name="sizeHint" stdset="0" >
739+ <size>
740+ <width>116</width>
741+ <height>20</height>
742+ </size>
743+ </property>
744+ </spacer>
745+ </item>
746+ </layout>
747+ </widget>
748+ <widget class="QWidget" name="page_2" >
749+ <layout class="QGridLayout" name="gridLayout_3" >
750+ <item row="0" column="0" colspan="2" >
751+ <widget class="QLabel" name="label" >
752+ <property name="text" >
753+ <string>Please tweak the control for:</string>
754+ </property>
755+ </widget>
756+ </item>
757+ <item row="1" column="0" colspan="2" >
758+ <widget class="QLabel" name="labelMixxxControl" >
759+ <property name="font" >
760+ <font>
761+ <weight>75</weight>
762+ <bold>true</bold>
763+ </font>
764+ </property>
765+ <property name="text" >
766+ <string>Mixxx Control</string>
767+ </property>
768+ <property name="alignment" >
769+ <set>Qt::AlignCenter</set>
770+ </property>
771+ </widget>
772+ </item>
773+ <item row="2" column="0" colspan="2" >
774+ <widget class="QLabel" name="labelMappedTo" >
775+ <property name="text" >
776+ <string>Successfully mapped to: </string>
777+ </property>
778+ <property name="alignment" >
779+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
780+ </property>
781+ </widget>
782+ </item>
783+ <item row="4" column="0" >
784+ <widget class="QPushButton" name="pushButtonPrevious" >
785+ <property name="enabled" >
786+ <bool>false</bool>
787+ </property>
788+ <property name="text" >
789+ <string>Previous</string>
790+ </property>
791+ </widget>
792+ </item>
793+ <item row="4" column="1" >
794+ <widget class="QPushButton" name="pushButtonSkip" >
795+ <property name="text" >
796+ <string>Skip</string>
797+ </property>
798+ </widget>
799+ </item>
800+ <item row="5" column="0" colspan="2" >
801+ <widget class="QLabel" name="label_6" >
802+ <property name="text" >
803+ <string>Press spacebar to proceed or skip.</string>
804+ </property>
805+ <property name="alignment" >
806+ <set>Qt::AlignCenter</set>
807+ </property>
808+ </widget>
809+ </item>
810+ <item row="3" column="0" >
811+ <spacer name="verticalSpacer" >
812+ <property name="orientation" >
813+ <enum>Qt::Vertical</enum>
814+ </property>
815+ <property name="sizeHint" stdset="0" >
816+ <size>
817+ <width>20</width>
818+ <height>40</height>
819+ </size>
820+ </property>
821+ </spacer>
822+ </item>
823+ </layout>
824+ </widget>
825+ <widget class="QWidget" name="page_3" >
826+ <layout class="QGridLayout" name="gridLayout_4" >
827+ <item row="0" column="0" colspan="3" >
828+ <widget class="QLabel" name="label_4" >
829+ <property name="font" >
830+ <font>
831+ <weight>75</weight>
832+ <bold>true</bold>
833+ </font>
834+ </property>
835+ <property name="text" >
836+ <string>MIDI learning complete!</string>
837+ </property>
838+ <property name="alignment" >
839+ <set>Qt::AlignCenter</set>
840+ </property>
841+ </widget>
842+ </item>
843+ <item row="1" column="0" colspan="3" >
844+ <widget class="QLabel" name="label_8" >
845+ <property name="text" >
846+ <string>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
847+&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
848+p, li { white-space: pre-wrap; }
849+&lt;/style>&lt;/head>&lt;body style=" font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;">
850+&lt;p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This wizard can be re-run at any time.&lt;/p>
851+&lt;p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;br />Now go spin some beats!&lt;/p>&lt;/body>&lt;/html></string>
852+ </property>
853+ <property name="wordWrap" >
854+ <bool>true</bool>
855+ </property>
856+ </widget>
857+ </item>
858+ <item row="2" column="0" >
859+ <spacer name="horizontalSpacer_3" >
860+ <property name="orientation" >
861+ <enum>Qt::Horizontal</enum>
862+ </property>
863+ <property name="sizeHint" stdset="0" >
864+ <size>
865+ <width>109</width>
866+ <height>20</height>
867+ </size>
868+ </property>
869+ </spacer>
870+ </item>
871+ <item row="2" column="1" >
872+ <widget class="QPushButton" name="pushButtonFinish" >
873+ <property name="text" >
874+ <string>Finito!</string>
875+ </property>
876+ </widget>
877+ </item>
878+ <item row="2" column="2" >
879+ <spacer name="horizontalSpacer_4" >
880+ <property name="orientation" >
881+ <enum>Qt::Horizontal</enum>
882+ </property>
883+ <property name="sizeHint" stdset="0" >
884+ <size>
885+ <width>109</width>
886+ <height>20</height>
887+ </size>
888+ </property>
889+ </spacer>
890+ </item>
891+ </layout>
892+ </widget>
893+ </widget>
894+ </item>
895+ </layout>
896+ </widget>
897+ <resources/>
898+ <connections/>
899+</ui>
900
901=== added file 'mixxx/src/dlgprefmidibindings.cpp'
902--- mixxx/src/dlgprefmidibindings.cpp 1970-01-01 00:00:00 +0000
903+++ mixxx/src/dlgprefmidibindings.cpp 2012-06-27 11:06:21 +0000
904@@ -0,0 +1,510 @@
905+/***************************************************************************
906+ dlgprefmidibindings.cpp - description
907+ -------------------
908+ begin : Sat Jun 21 2008
909+ copyright : (C) 2008 by Tom Care
910+ email : psyc0de@gmail.com
911+ ***************************************************************************/
912+
913+/***************************************************************************
914+ * *
915+ * This program is free software; you can redistribute it and/or modify *
916+ * it under the terms of the GNU General Public License as published by *
917+ * the Free Software Foundation; either version 2 of the License, or *
918+ * (at your option) any later version. *
919+ * *
920+ ***************************************************************************/
921+#include <QtGui>
922+#include <QDebug>
923+#include "midi/midiinputmappingtablemodel.h"
924+#include "midi/midioutputmappingtablemodel.h"
925+#include "midi/midichanneldelegate.h"
926+#include "midi/midistatusdelegate.h"
927+#include "midi/midinodelegate.h"
928+#include "midi/midioptiondelegate.h"
929+#include "controlgroupdelegate.h"
930+#include "controlvaluedelegate.h"
931+#include "dlgprefmidibindings.h"
932+#include "midi/mididevice.h"
933+#include "midi/mididevicemanager.h"
934+#include "configobject.h"
935+#include "midi/midimapping.h"
936+
937+#ifdef __MIDISCRIPT__
938+#include "midi/midiscriptengine.h"
939+#endif
940+
941+
942+DlgPrefMidiBindings::DlgPrefMidiBindings(QWidget *parent, MidiDevice* midiDevice,
943+ MidiDeviceManager* midiDeviceManager,
944+ ConfigObject<ConfigValue> *pConfig)
945+ : QWidget(parent), Ui::DlgPrefMidiBindingsDlg()
946+{
947+ setupUi(this);
948+ m_pConfig = pConfig;
949+ m_pMidiDevice = midiDevice;
950+ m_pMidiDeviceManager = midiDeviceManager;
951+
952+ m_pDlgMidiLearning = NULL;
953+
954+ m_bDirty = false;
955+
956+ labelDeviceName->setText(m_pMidiDevice->getName());
957+
958+ //Tell the input mapping table widget which data model it should be viewing
959+ //(note that m_pInputMappingTableView is defined in the .ui file!)
960+ m_pInputMappingTableView->setModel((QAbstractItemModel*)m_pMidiDevice->getMidiMapping()->getMidiInputMappingTableModel());
961+
962+ m_pInputMappingTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
963+ m_pInputMappingTableView->setSelectionMode(QAbstractItemView::ContiguousSelection); //The model won't like ExtendedSelection, probably.
964+ m_pInputMappingTableView->verticalHeader()->hide();
965+
966+ //Set up "delete" as a shortcut key to remove a row for the MIDI input table.
967+ m_deleteMIDIInputRowAction = new QAction(m_pInputMappingTableView);
968+ /*m_deleteMIDIInputRowAction->setShortcut(QKeySequence::Delete);
969+ m_deleteMIDIInputRowAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
970+ connect(m_deleteMIDIInputRowAction, SIGNAL(triggered()), this, SLOT(slotRemoveInputBinding()));
971+ */
972+ //The above shortcut doesn't work yet, not quite sure why. -- Albert Feb 1 / 2009
973+
974+ //Set up the cool item delegates for the input mapping table
975+ m_pMidiChannelDelegate = new MidiChannelDelegate(m_pInputMappingTableView);
976+ m_pMidiStatusDelegate = new MidiStatusDelegate(m_pInputMappingTableView);
977+ m_pMidiNoDelegate = new MidiNoDelegate(m_pInputMappingTableView);
978+ m_pMidiOptionDelegate = new MidiOptionDelegate(m_pInputMappingTableView);
979+ m_pControlGroupDelegate = new ControlGroupDelegate(m_pInputMappingTableView);
980+ m_pControlValueDelegate = new ControlValueDelegate(m_pInputMappingTableView);
981+ m_pInputMappingTableView->setItemDelegateForColumn(MIDIINPUTTABLEINDEX_MIDISTATUS, m_pMidiStatusDelegate);
982+ m_pInputMappingTableView->setItemDelegateForColumn(MIDIINPUTTABLEINDEX_MIDICHANNEL, m_pMidiChannelDelegate);
983+ m_pInputMappingTableView->setItemDelegateForColumn(MIDIINPUTTABLEINDEX_MIDINO, m_pMidiNoDelegate);
984+ m_pInputMappingTableView->setItemDelegateForColumn(MIDIINPUTTABLEINDEX_CONTROLOBJECTGROUP, m_pControlGroupDelegate);
985+ m_pInputMappingTableView->setItemDelegateForColumn(MIDIINPUTTABLEINDEX_CONTROLOBJECTVALUE, m_pControlValueDelegate);
986+ m_pInputMappingTableView->setItemDelegateForColumn(MIDIINPUTTABLEINDEX_MIDIOPTION, m_pMidiOptionDelegate);
987+
988+ //Tell the output mapping table widget which data model it should be viewing
989+ //(note that m_pOutputMappingTableView is defined in the .ui file!)
990+ m_pOutputMappingTableView->setModel((QAbstractItemModel*)m_pMidiDevice->getMidiMapping()->getMidiOutputMappingTableModel());
991+ m_pOutputMappingTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
992+ m_pOutputMappingTableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
993+ m_pOutputMappingTableView->verticalHeader()->hide();
994+
995+ //Set up the cool item delegates for the output mapping table
996+ m_pOutputMappingTableView->setItemDelegateForColumn(MIDIOUTPUTTABLEINDEX_MIDISTATUS, m_pMidiStatusDelegate);
997+ m_pOutputMappingTableView->setItemDelegateForColumn(MIDIOUTPUTTABLEINDEX_MIDICHANNEL, m_pMidiChannelDelegate);
998+ m_pOutputMappingTableView->setItemDelegateForColumn(MIDIOUTPUTTABLEINDEX_MIDINO, m_pMidiNoDelegate);
999+ //TODO: We need different delegates for the output table's CO group/value columns because we only list real input
1000+ // controls, and for output we'd want to list a different set with stuff like "VUMeter" and other output controls.
1001+ //m_pOutputMappingTableView->setItemDelegateForColumn(MIDIOUTPUTTABLEINDEX_CONTROLOBJECTGROUP, m_pControlGroupDelegate);
1002+ //m_pOutputMappingTableView->setItemDelegateForColumn(MIDIOUTPUTTABLEINDEX_CONTROLOBJECTVALUE, m_pControlValueDelegate);
1003+
1004+ // Connect buttons to slots
1005+ connect(btnExportXML, SIGNAL(clicked()), this, SLOT(slotExportXML()));
1006+
1007+ //Input bindings
1008+ connect(btnMidiLearnWizard, SIGNAL(clicked()), this, SLOT(slotShowMidiLearnDialog()));
1009+ connect(btnMidiLearnWizard, SIGNAL(clicked()), this, SLOT(slotDirty()));
1010+ connect(btnClearAllInputBindings, SIGNAL(clicked()), this, SLOT(slotClearAllInputBindings()));
1011+ connect(btnClearAllInputBindings, SIGNAL(clicked()), this, SLOT(slotDirty()));
1012+ connect(btnRemoveInputBinding, SIGNAL(clicked()), this, SLOT(slotRemoveInputBinding()));
1013+ connect(btnRemoveInputBinding, SIGNAL(clicked()), this, SLOT(slotDirty()));
1014+ connect(btnAddInputBinding, SIGNAL(clicked()), this, SLOT(slotAddInputBinding()));
1015+ connect(btnAddInputBinding, SIGNAL(clicked()), this, SLOT(slotDirty()));
1016+
1017+ //Output bindings
1018+ connect(btnClearAllOutputBindings, SIGNAL(clicked()), this, SLOT(slotClearAllOutputBindings()));
1019+ connect(btnClearAllOutputBindings, SIGNAL(clicked()), this, SLOT(slotDirty()));
1020+ connect(btnRemoveOutputBinding, SIGNAL(clicked()), this, SLOT(slotRemoveOutputBinding()));
1021+ connect(btnRemoveOutputBinding, SIGNAL(clicked()), this, SLOT(slotDirty()));
1022+ connect(btnAddOutputBinding, SIGNAL(clicked()), this, SLOT(slotAddOutputBinding()));
1023+ connect(btnAddOutputBinding, SIGNAL(clicked()), this, SLOT(slotDirty()));
1024+
1025+ connect(comboBoxPreset, SIGNAL(activated(const QString&)), this, SLOT(slotLoadMidiMapping(const QString&)));
1026+ connect(comboBoxPreset, SIGNAL(activated(const QString&)), this, SLOT(slotDirty()));
1027+
1028+ connect(m_pInputMappingTableView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(slotDirty()));
1029+ connect(m_pOutputMappingTableView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(slotDirty()));
1030+
1031+ //Load the list of presets into the presets combobox.
1032+ enumeratePresets();
1033+
1034+ //Initialize the output device combobox
1035+ enumerateOutputDevices();
1036+
1037+}
1038+
1039+DlgPrefMidiBindings::~DlgPrefMidiBindings() {
1040+ delete m_deleteMIDIInputRowAction;
1041+}
1042+
1043+void DlgPrefMidiBindings::slotDirty ()
1044+{
1045+ m_bDirty = true;
1046+}
1047+
1048+void DlgPrefMidiBindings::enumerateOutputDevices()
1049+{
1050+ comboBoxOutputDevice->clear();
1051+
1052+ comboBoxOutputDevice->addItem(tr("None"));
1053+
1054+ //For each MIDI output device, insert an item into the output device combobox.
1055+ QList<MidiDevice*> deviceList = m_pMidiDeviceManager->getDeviceList(true, false);
1056+ QListIterator<MidiDevice*> it(deviceList);
1057+
1058+ while (it.hasNext())
1059+ {
1060+ MidiDevice* currentDevice = it.next();
1061+ QString curDeviceName = currentDevice->getName();
1062+ //qDebug() << "curDeviceName: " << curDeviceName;
1063+ comboBoxOutputDevice->addItem(curDeviceName);
1064+ }
1065+
1066+ //Assume autopairing was done and let's just show the output device combobox with the name
1067+ //of the input device selected for now...
1068+ QString currentOutputMidiDeviceName = m_pMidiDevice->getName();
1069+ comboBoxOutputDevice->setCurrentIndex(comboBoxOutputDevice->findText(currentOutputMidiDeviceName));
1070+
1071+}
1072+
1073+void DlgPrefMidiBindings::enumeratePresets()
1074+{
1075+ QList<QString> presetsList;
1076+ comboBoxPreset->clear();
1077+
1078+ //Insert a dummy "..." item at the top to try to make it less confusing.
1079+ //(For example, we don't want "Akai MPD24" showing up as the default item
1080+ // when a user has their controller plugged in)
1081+ comboBoxPreset->addItem("...");
1082+
1083+ // paths to search for midi presets
1084+ QList<QString> midiDirPaths;
1085+ midiDirPaths.append(LPRESETS_PATH);
1086+ midiDirPaths.append(m_pConfig->getConfigPath().append("midi/"));
1087+
1088+ QListIterator<QString> itpth(midiDirPaths);
1089+ while (itpth.hasNext()) {
1090+ QDirIterator it(itpth.next());
1091+ while (it.hasNext())
1092+ {
1093+ it.next(); //Advance iterator. We get the filename from the next line. (It's a bit weird.)
1094+ QString curMapping = it.fileName();
1095+ if (curMapping.endsWith(MIDI_MAPPING_EXTENSION)) //blah, thanks for nothing Qt
1096+ {
1097+ curMapping.chop(QString(MIDI_MAPPING_EXTENSION).length()); //chop off the .midi.xml
1098+ presetsList.append(curMapping);
1099+ }
1100+ }
1101+ }
1102+ //Sort in alphabetical order
1103+ qSort(presetsList);
1104+ comboBoxPreset->addItems(presetsList);
1105+}
1106+
1107+
1108+
1109+/* slotUpdate()
1110+ * Called when the dialog is displayed.
1111+ */
1112+void DlgPrefMidiBindings::slotUpdate() {
1113+
1114+ //Check if the device that this dialog is for is already enabled...
1115+ if (m_pMidiDevice->isOpen())
1116+ {
1117+ chkEnabledDevice->setCheckState(Qt::Checked); //Check the "Enabled" box
1118+ toolBox->setEnabled(true); //Enable MIDI in/out toolbox.
1119+ groupBoxPresets->setEnabled(true); //Enable presets group box.
1120+ }
1121+ else {
1122+ chkEnabledDevice->setCheckState(Qt::Unchecked); //Uncheck the "Enabled" box
1123+ toolBox->setEnabled(false); //Disable MIDI in/out toolbox.
1124+ groupBoxPresets->setEnabled(false); //Disable presets group box.
1125+ }
1126+
1127+ //Connect the "Enabled" checkbox after the checkbox state is set
1128+ connect(chkEnabledDevice, SIGNAL(stateChanged(int)), this, SLOT(slotDeviceState(int)));
1129+ connect(chkEnabledDevice, SIGNAL(stateChanged(int)), this, SLOT(slotDirty()));
1130+}
1131+
1132+/* slotApply()
1133+ * Called when the OK button is pressed.
1134+ */
1135+void DlgPrefMidiBindings::slotApply() {
1136+ /* User has pressed OK, so enable or disable the device, write the
1137+ * controls to the DOM, and reload the MIDI bindings. FIXED: only
1138+ * do this if the user has changed the preferences.
1139+ */
1140+ if (m_bDirty)
1141+ {
1142+ m_pMidiDevice->disableMidiLearn();
1143+ if (chkEnabledDevice->isChecked()) {
1144+ //Enable the device.
1145+ enableDevice();
1146+
1147+ //Disable processing of MIDI messages received from the device in order to
1148+ //prevent a race condition while we modify the MIDI mapping.
1149+ m_pMidiDevice->setReceiveInhibit(true);
1150+ m_pMidiDevice->getMidiMapping()->applyPreset();
1151+ m_pMidiDevice->setReceiveInhibit(false);
1152+
1153+ //FIXME: We need some logic like this to make changing the output device work.
1154+ // See MidiDeviceManager::associateInputAndOutputDevices() for more info...
1155+ /*
1156+ if (comboBoxOutputDevice->currentText() != tr("None"))
1157+ m_pMidiDeviceManager->associateInputAndOutputDevices(m_pMidiDevice, comboBoxOutputDevice->currentText());
1158+ */
1159+ }
1160+ else disableDevice();
1161+ }
1162+ m_bDirty = false;
1163+}
1164+
1165+void DlgPrefMidiBindings::slotShowMidiLearnDialog() {
1166+
1167+ //If the user has checked the "Enabled" checkbox but they haven't
1168+ //hit OK to apply it yet, prompt them to apply the settings before we open
1169+ //the MIDI learning dialog. If we don't apply the settings first and open the device,
1170+ //MIDI learn won't react to MIDI messages.
1171+ if (chkEnabledDevice->isChecked() && !m_pMidiDevice->isOpen())
1172+ {
1173+ QMessageBox::StandardButton result = QMessageBox::question(this,
1174+ tr("Apply MIDI device settings?"),
1175+ tr("Your settings must be applied before starting the MIDI learning wizard.\n"
1176+ "Apply settings and continue?"));
1177+ if (result == QMessageBox::Cancel)
1178+ {
1179+ return;
1180+ }
1181+ else
1182+ {
1183+ slotApply();
1184+ }
1185+ }
1186+
1187+ //Note that DlgMidiLearning is set to delete itself on
1188+ //close using the Qt::WA_DeleteOnClose attribute (so this "new" doesn't leak memory)
1189+ m_pDlgMidiLearning = new DlgMidiLearning(this, m_pMidiDevice->getMidiMapping());
1190+ m_pDlgMidiLearning->show();
1191+}
1192+
1193+/* slotImportXML()
1194+ * Prompts the user for an XML preset and loads it.
1195+ */
1196+void DlgPrefMidiBindings::slotLoadMidiMapping(const QString &name) {
1197+
1198+ if (name == "...")
1199+ return;
1200+
1201+ //Ask for confirmation if the MIDI tables aren't empty...
1202+ MidiMapping* mapping = m_pMidiDevice->getMidiMapping();
1203+ if (mapping->numInputMidiMessages() > 0 ||
1204+ mapping->numOutputMixxxControls() > 0)
1205+ {
1206+ QMessageBox::StandardButton result = QMessageBox::question(
1207+ this,
1208+ tr("Overwrite existing mapping?"),
1209+ tr("Are you sure you'd like to load the %1 mapping?\n"
1210+ "This will overwrite your existing MIDI mapping.").arg(name),
1211+ QMessageBox::Yes | QMessageBox::No);
1212+
1213+ if (result == QMessageBox::No) {
1214+ //Select the "..." item again in the combobox.
1215+ comboBoxPreset->setCurrentIndex(0);
1216+ return;
1217+ }
1218+ }
1219+
1220+ QString filename = LPRESETS_PATH + name + MIDI_MAPPING_EXTENSION;
1221+ QFile ftest(filename);
1222+ if ( !ftest.exists() ) filename = m_pConfig->getConfigPath().append("midi/") + name + MIDI_MAPPING_EXTENSION;
1223+
1224+ if (!filename.isNull()) m_pMidiDevice->getMidiMapping()->loadPreset(filename, true); // It's applied on prefs close
1225+ m_pInputMappingTableView->update();
1226+
1227+ //Select the "..." item again in the combobox.
1228+ comboBoxPreset->setCurrentIndex(0);
1229+}
1230+
1231+/* slotExportXML()
1232+ * Prompts the user for an XML preset and saves it.
1233+ */
1234+void DlgPrefMidiBindings::slotExportXML() {
1235+ QString fileName = QFileDialog::getSaveFileName(this,
1236+ tr("Export Mixxx MIDI Bindings"), m_pConfig->getConfigPath().append("midi/"),
1237+ tr("Preset Files (*.midi.xml)"));
1238+ if (!fileName.isNull()) m_pMidiDevice->getMidiMapping()->savePreset(fileName);
1239+}
1240+
1241+void DlgPrefMidiBindings::slotDeviceState(int state) {
1242+ if (state == Qt::Checked) {
1243+ toolBox->setEnabled(true); //Enable MIDI in/out toolbox.
1244+ groupBoxPresets->setEnabled(true); //Enable presets group box.
1245+ emit deviceStateChanged(this,true); // Set tree item text to bold
1246+ }
1247+ else {
1248+ toolBox->setEnabled(false); //Disable MIDI in/out toolbox.
1249+ groupBoxPresets->setEnabled(false); //Disable presets group box.
1250+ emit deviceStateChanged(this,false); // Set tree item text to not bold
1251+ }
1252+}
1253+
1254+void DlgPrefMidiBindings::enableDevice()
1255+{
1256+ if (m_pMidiDevice->isOpen()) {
1257+ m_pMidiDevice->close();
1258+ }
1259+ m_pMidiDevice->open();
1260+ m_pConfig->set(ConfigKey("[Midi]", m_pMidiDevice->getName().replace(" ", "_")), 1);
1261+
1262+ //TODO: Should probably check if open() actually succeeded.
1263+}
1264+
1265+void DlgPrefMidiBindings::disableDevice()
1266+{
1267+ m_pMidiDevice->close();
1268+ m_pConfig->set(ConfigKey("[Midi]", m_pMidiDevice->getName().replace(" ", "_")), 0);
1269+
1270+ //TODO: Should probably check if close() actually succeeded.
1271+}
1272+
1273+void DlgPrefMidiBindings::slotAddInputBinding()
1274+{
1275+ bool ok = true;
1276+ QString controlGroup = QInputDialog::getItem(this, tr("Select Control Group"), tr("Select Control Group"),
1277+ ControlGroupDelegate::getControlGroups(), 0, false, &ok);
1278+ if (!ok) return;
1279+
1280+ QStringList controlValues;
1281+ if (controlGroup == CONTROLGROUP_CHANNEL1_STRING ||
1282+ controlGroup == CONTROLGROUP_CHANNEL2_STRING ||
1283+ controlGroup == CONTROLGROUP_SAMPLER1_STRING ||
1284+ controlGroup == CONTROLGROUP_SAMPLER2_STRING ||
1285+ controlGroup == CONTROLGROUP_SAMPLER3_STRING ||
1286+ controlGroup == CONTROLGROUP_SAMPLER4_STRING) {
1287+ controlValues = ControlValueDelegate::getChannelControlValues();
1288+ }
1289+ else if (controlGroup == CONTROLGROUP_MASTER_STRING)
1290+ {
1291+ controlValues = ControlValueDelegate::getMasterControlValues();
1292+ }
1293+ else if (controlGroup == CONTROLGROUP_PLAYLIST_STRING)
1294+ {
1295+ controlValues = ControlValueDelegate::getPlaylistControlValues();
1296+ }
1297+ else if (controlGroup == CONTROLGROUP_FLANGER_STRING)
1298+ {
1299+ controlValues = ControlValueDelegate::getFlangerControlValues();
1300+ }
1301+ else if (controlGroup == CONTROLGROUP_MICROPHONE_STRING)
1302+ {
1303+ controlValues = ControlValueDelegate::getMicrophoneControlValues();
1304+ }
1305+ else
1306+ {
1307+ qDebug() << "Unhandled ControlGroup in " << __FILE__;
1308+ }
1309+
1310+
1311+ QString controlValue = QInputDialog::getItem(this, tr("Select Control"), tr("Select Control"),
1312+ controlValues, 0, false, &ok);
1313+ if (!ok) return;
1314+
1315+
1316+ MixxxControl mixxxControl(controlGroup, controlValue);
1317+ MidiMessage message;
1318+
1319+ while (m_pMidiDevice->getMidiMapping()->isMidiMessageMapped(message))
1320+ {
1321+ message.setMidiNo(message.getMidiNo() + 1);
1322+ if (message.getMidiNo() >= 127) //If the table is full, then overwrite something...
1323+ break;
1324+ }
1325+ m_pMidiDevice->getMidiMapping()->setInputMidiMapping(message, mixxxControl);
1326+}
1327+
1328+void DlgPrefMidiBindings::slotRemoveInputBinding()
1329+{
1330+ QModelIndexList selectedIndices = m_pInputMappingTableView->selectionModel()->selectedRows();
1331+ if (selectedIndices.size() > 0)
1332+ {
1333+ MidiInputMappingTableModel* tableModel = dynamic_cast<MidiInputMappingTableModel*>(m_pInputMappingTableView->model());
1334+ if (tableModel) {
1335+
1336+ QModelIndex curIndex;
1337+ //The model indices are sorted so that we remove the rows from the table
1338+ //in ascending order. This is necessary because if row A is above row B in
1339+ //the table, and you remove row A, the model index for row B will change.
1340+ //Sorting the indices first means we don't have to worry about this.
1341+ qSort(selectedIndices);
1342+
1343+ //Going through the model indices in descending order (see above comment for explanation).
1344+ QListIterator<QModelIndex> it(selectedIndices);
1345+ it.toBack();
1346+ while (it.hasPrevious())
1347+ {
1348+ curIndex = it.previous();
1349+ tableModel->removeRow(curIndex.row());
1350+ }
1351+ }
1352+ }
1353+}
1354+
1355+void DlgPrefMidiBindings::slotClearAllInputBindings() {
1356+ if (QMessageBox::warning(this, tr("Clear Input Bindings"),
1357+ tr("Are you sure you want to clear all bindings?"),
1358+ QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Ok)
1359+ return;
1360+
1361+ //Remove all the rows from the data model (ie. the MIDI mapping).
1362+ MidiInputMappingTableModel* tableModel = dynamic_cast<MidiInputMappingTableModel*>(m_pInputMappingTableView->model());
1363+ if (tableModel) {
1364+ tableModel->removeRows(0, tableModel->rowCount());
1365+ }
1366+}
1367+
1368+
1369+void DlgPrefMidiBindings::slotAddOutputBinding() {
1370+ qDebug() << "STUB: DlgPrefMidiBindings::slotAddOutputBinding()";
1371+
1372+ m_pMidiDevice->getMidiMapping()->setOutputMidiMapping(MixxxControl(), MidiMessage());
1373+}
1374+
1375+void DlgPrefMidiBindings::slotRemoveOutputBinding()
1376+{
1377+ QModelIndexList selectedIndices = m_pOutputMappingTableView->selectionModel()->selectedRows();
1378+ if (selectedIndices.size() > 0)
1379+ {
1380+ MidiOutputMappingTableModel* tableModel =
1381+ dynamic_cast<MidiOutputMappingTableModel*>(m_pOutputMappingTableView->model());
1382+ if (tableModel) {
1383+ QModelIndex curIndex;
1384+ //The model indices are sorted so that we remove the rows from the table
1385+ //in ascending order. This is necessary because if row A is above row B in
1386+ //the table, and you remove row A, the model index for row B will change.
1387+ //Sorting the indices first means we don't have to worry about this.
1388+ //qSort(selectedIndices);
1389+
1390+ //Going through the model indices in descending order (see above comment for explanation).
1391+ QListIterator<QModelIndex> it(selectedIndices);
1392+ it.toBack();
1393+ while (it.hasPrevious())
1394+ {
1395+ curIndex = it.previous();
1396+ qDebug() << "Dlg: removing row" << curIndex.row();
1397+ tableModel->removeRow(curIndex.row());
1398+ }
1399+ }
1400+ }
1401+}
1402+
1403+void DlgPrefMidiBindings::slotClearAllOutputBindings() {
1404+ if (QMessageBox::warning(this, tr("Clear Output Bindings"),
1405+ tr("Are you sure you want to clear all output bindings?"),
1406+ QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Ok)
1407+ return;
1408+
1409+ //Remove all the rows from the data model (ie. the MIDI mapping).
1410+ MidiOutputMappingTableModel* tableModel = dynamic_cast<MidiOutputMappingTableModel*>(m_pOutputMappingTableView->model());
1411+ if (tableModel) {
1412+ tableModel->removeRows(0, tableModel->rowCount());
1413+ }
1414+}
1415
1416=== added file 'mixxx/src/dlgprefmidibindings.h'
1417--- mixxx/src/dlgprefmidibindings.h 1970-01-01 00:00:00 +0000
1418+++ mixxx/src/dlgprefmidibindings.h 2012-06-27 11:06:21 +0000
1419@@ -0,0 +1,92 @@
1420+/***************************************************************************
1421+ dlgprefmidibindings.h - description
1422+ -------------------
1423+ begin : Sat Jun 21 2008
1424+ copyright : (C) 2008 by Tom Care
1425+ email : psyc0de@gmail.com
1426+ ***************************************************************************/
1427+
1428+/***************************************************************************
1429+ * *
1430+ * This program is free software; you can redistribute it and/or modify *
1431+ * it under the terms of the GNU General Public License as published by *
1432+ * the Free Software Foundation; either version 2 of the License, or *
1433+ * (at your option) any later version. *
1434+ * *
1435+ ***************************************************************************/
1436+#ifndef DLGPREFMIDIBINDINGS_H_
1437+#define DLGPREFMIDIBINDINGS_H_
1438+
1439+#include <QtGui>
1440+#include "ui_dlgprefmidibindingsdlg.h"
1441+#include "dlgmidilearning.h"
1442+#include "configobject.h"
1443+
1444+//Forward declarations
1445+class MidiChannelDelegate;
1446+class MidiStatusDelegate;
1447+class MidiNoDelegate;
1448+class MidiOptionDelegate;
1449+class ControlGroupDelegate;
1450+class ControlValueDelegate;
1451+class MidiDevice;
1452+class MidiDeviceManager;
1453+
1454+class DlgPrefMidiBindings : public QWidget, public Ui::DlgPrefMidiBindingsDlg {
1455+ Q_OBJECT
1456+public:
1457+ DlgPrefMidiBindings(QWidget *parent, MidiDevice* midiDevice,
1458+ MidiDeviceManager* midiDeviceManager,
1459+ ConfigObject<ConfigValue> *pConfig);
1460+ ~DlgPrefMidiBindings();
1461+
1462+
1463+public slots:
1464+ void slotUpdate();
1465+ void slotApply();
1466+ void slotShowMidiLearnDialog();
1467+ void slotLoadMidiMapping(const QString &name);
1468+ void slotExportXML();
1469+ void slotDeviceState(int state);
1470+
1471+ //Input bindings
1472+ void slotClearAllInputBindings();
1473+ void slotRemoveInputBinding();
1474+ void slotAddInputBinding();
1475+
1476+ //Output bindings
1477+ void slotAddOutputBinding();
1478+ void slotClearAllOutputBindings();
1479+ void slotRemoveOutputBinding();
1480+
1481+ //Mark that we need to apply the settings.
1482+ void slotDirty ();
1483+
1484+signals:
1485+ void deviceStateChanged(DlgPrefMidiBindings*, bool);
1486+
1487+private:
1488+ void setRowBackground(int row, QColor color);
1489+ void savePreset(QString path);
1490+ void enumeratePresets();
1491+ void enumerateOutputDevices();
1492+
1493+ void enableDevice();
1494+ void disableDevice();
1495+
1496+ bool m_bDirty;
1497+ int currentGroupRow;
1498+ MidiChannelDelegate* m_pMidiChannelDelegate;
1499+ MidiStatusDelegate* m_pMidiStatusDelegate;
1500+ MidiNoDelegate* m_pMidiNoDelegate;
1501+ MidiOptionDelegate* m_pMidiOptionDelegate;
1502+ ControlGroupDelegate* m_pControlGroupDelegate;
1503+ ControlValueDelegate* m_pControlValueDelegate;
1504+ QAction* m_deleteMIDIInputRowAction; /** Used for setting up the shortcut for delete button */
1505+ ConfigObject<ConfigValue> *m_pConfig;
1506+ MidiDevice* m_pMidiDevice;
1507+ MidiDeviceManager* m_pMidiDeviceManager;
1508+ DlgMidiLearning* m_pDlgMidiLearning;
1509+};
1510+
1511+#endif /*DLGPREFMIDIBINDINGS_H_*/
1512
1513=== added file 'mixxx/src/dlgprefmidibindingsdlg.ui'
1514--- mixxx/src/dlgprefmidibindingsdlg.ui 1970-01-01 00:00:00 +0000
1515+++ mixxx/src/dlgprefmidibindingsdlg.ui 2012-06-27 11:06:21 +0000
1516@@ -0,0 +1,324 @@
1517+<?xml version="1.0" encoding="UTF-8"?>
1518+<ui version="4.0">
1519+ <class>DlgPrefMidiBindingsDlg</class>
1520+ <widget class="QWidget" name="DlgPrefMidiBindingsDlg">
1521+ <property name="geometry">
1522+ <rect>
1523+ <x>0</x>
1524+ <y>0</y>
1525+ <width>598</width>
1526+ <height>436</height>
1527+ </rect>
1528+ </property>
1529+ <property name="windowTitle">
1530+ <string>Dialog</string>
1531+ </property>
1532+ <layout class="QGridLayout" name="gridLayout_4">
1533+ <item row="0" column="3" rowspan="3">
1534+ <widget class="QGroupBox" name="groupBoxPresets">
1535+ <property name="title">
1536+ <string/>
1537+ </property>
1538+ <property name="flat">
1539+ <bool>true</bool>
1540+ </property>
1541+ <property name="checkable">
1542+ <bool>false</bool>
1543+ </property>
1544+ <layout class="QGridLayout" name="gridLayout">
1545+ <property name="margin">
1546+ <number>0</number>
1547+ </property>
1548+ <item row="1" column="1">
1549+ <widget class="QLabel" name="label">
1550+ <property name="text">
1551+ <string>Load Preset:</string>
1552+ </property>
1553+ <property name="alignment">
1554+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
1555+ </property>
1556+ <property name="buddy" >
1557+ <cstring>comboBoxPreset</cstring>
1558+ </property>
1559+ </widget>
1560+ </item>
1561+ <item row="1" column="2">
1562+ <widget class="QComboBox" name="comboBoxPreset">
1563+ <property name="sizePolicy">
1564+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1565+ <horstretch>0</horstretch>
1566+ <verstretch>0</verstretch>
1567+ </sizepolicy>
1568+ </property>
1569+ <property name="maximumSize">
1570+ <size>
1571+ <width>250</width>
1572+ <height>16777215</height>
1573+ </size>
1574+ </property>
1575+ </widget>
1576+ </item>
1577+ <item row="1" column="3">
1578+ <widget class="QPushButton" name="btnExportXML">
1579+ <property name="styleSheet">
1580+ <string notr="true"/>
1581+ </property>
1582+ <property name="text">
1583+ <string>Export</string>
1584+ </property>
1585+ </widget>
1586+ </item>
1587+ <item row="2" column="2">
1588+ <widget class="QComboBox" name="comboBoxOutputDevice">
1589+ <property name="enabled">
1590+ <bool>false</bool>
1591+ </property>
1592+ <property name="maximumSize">
1593+ <size>
1594+ <width>250</width>
1595+ <height>16777215</height>
1596+ </size>
1597+ </property>
1598+ </widget>
1599+ </item>
1600+ <item row="2" column="1">
1601+ <widget class="QLabel" name="label_2">
1602+ <property name="text">
1603+ <string>Output:</string>
1604+ </property>
1605+ <property name="buddy" >
1606+ <cstring>comboBoxOutputDevice</cstring>
1607+ </property>
1608+ </widget>
1609+ </item>
1610+ </layout>
1611+ </widget>
1612+ </item>
1613+ <item row="3" column="0" colspan="4">
1614+ <widget class="QToolBox" name="toolBox">
1615+ <property name="frameShape">
1616+ <enum>QFrame::NoFrame</enum>
1617+ </property>
1618+ <property name="currentIndex">
1619+ <number>0</number>
1620+ </property>
1621+ <property name="tabSpacing">
1622+ <number>0</number>
1623+ </property>
1624+ <widget class="QWidget" name="page">
1625+ <property name="geometry">
1626+ <rect>
1627+ <x>0</x>
1628+ <y>0</y>
1629+ <width>580</width>
1630+ <height>301</height>
1631+ </rect>
1632+ </property>
1633+ <attribute name="label">
1634+ <string>MIDI Input</string>
1635+ </attribute>
1636+ <layout class="QGridLayout" name="gridLayout_2">
1637+ <item row="1" column="0" colspan="2">
1638+ <widget class="QTableView" name="m_pInputMappingTableView"/>
1639+ </item>
1640+ <item row="2" column="0" colspan="2">
1641+ <widget class="QGroupBox" name="groupBoxInputManagement">
1642+ <property name="minimumSize">
1643+ <size>
1644+ <width>300</width>
1645+ <height>0</height>
1646+ </size>
1647+ </property>
1648+ <property name="title">
1649+ <string>Controls</string>
1650+ </property>
1651+ <layout class="QHBoxLayout" name="horizontalLayout_2">
1652+ <item>
1653+ <widget class="QPushButton" name="btnAddInputBinding">
1654+ <property name="styleSheet">
1655+ <string notr="true"/>
1656+ </property>
1657+ <property name="text">
1658+ <string>Add</string>
1659+ </property>
1660+ </widget>
1661+ </item>
1662+ <item>
1663+ <widget class="QPushButton" name="btnRemoveInputBinding">
1664+ <property name="styleSheet">
1665+ <string notr="true"/>
1666+ </property>
1667+ <property name="text">
1668+ <string>Remove</string>
1669+ </property>
1670+ </widget>
1671+ </item>
1672+ <item>
1673+ <widget class="QPushButton" name="btnMidiLearnWizard">
1674+ <property name="styleSheet">
1675+ <string notr="true"/>
1676+ </property>
1677+ <property name="text">
1678+ <string>MIDI Learning Wizard</string>
1679+ </property>
1680+ <property name="checkable">
1681+ <bool>false</bool>
1682+ </property>
1683+ <property name="checked">
1684+ <bool>false</bool>
1685+ </property>
1686+ </widget>
1687+ </item>
1688+ <item>
1689+ <spacer name="horizontalSpacer_3">
1690+ <property name="orientation">
1691+ <enum>Qt::Horizontal</enum>
1692+ </property>
1693+ <property name="sizeHint" stdset="0">
1694+ <size>
1695+ <width>219</width>
1696+ <height>20</height>
1697+ </size>
1698+ </property>
1699+ </spacer>
1700+ </item>
1701+ <item>
1702+ <widget class="QPushButton" name="btnClearAllInputBindings">
1703+ <property name="autoFillBackground">
1704+ <bool>false</bool>
1705+ </property>
1706+ <property name="styleSheet">
1707+ <string notr="true"/>
1708+ </property>
1709+ <property name="text">
1710+ <string>Clear All</string>
1711+ </property>
1712+ </widget>
1713+ </item>
1714+ </layout>
1715+ </widget>
1716+ </item>
1717+ </layout>
1718+ </widget>
1719+ <widget class="QWidget" name="page_2">
1720+ <property name="geometry">
1721+ <rect>
1722+ <x>0</x>
1723+ <y>0</y>
1724+ <width>234</width>
1725+ <height>137</height>
1726+ </rect>
1727+ </property>
1728+ <attribute name="label">
1729+ <string>MIDI Output</string>
1730+ </attribute>
1731+ <layout class="QGridLayout" name="gridLayout_3">
1732+ <item row="0" column="0">
1733+ <widget class="QTableView" name="m_pOutputMappingTableView">
1734+ <property name="enabled">
1735+ <bool>true</bool>
1736+ </property>
1737+ </widget>
1738+ </item>
1739+ <item row="1" column="0">
1740+ <widget class="QGroupBox" name="groupBoxOutputs">
1741+ <property name="title">
1742+ <string>Outputs</string>
1743+ </property>
1744+ <layout class="QHBoxLayout" name="horizontalLayout">
1745+ <item>
1746+ <widget class="QPushButton" name="btnAddOutputBinding">
1747+ <property name="enabled">
1748+ <bool>false</bool>
1749+ </property>
1750+ <property name="text">
1751+ <string>Add</string>
1752+ </property>
1753+ </widget>
1754+ </item>
1755+ <item>
1756+ <widget class="QPushButton" name="btnRemoveOutputBinding">
1757+ <property name="text">
1758+ <string>Remove</string>
1759+ </property>
1760+ </widget>
1761+ </item>
1762+ <item>
1763+ <spacer name="horizontalSpacer_2">
1764+ <property name="orientation">
1765+ <enum>Qt::Horizontal</enum>
1766+ </property>
1767+ <property name="sizeHint" stdset="0">
1768+ <size>
1769+ <width>219</width>
1770+ <height>20</height>
1771+ </size>
1772+ </property>
1773+ </spacer>
1774+ </item>
1775+ <item>
1776+ <widget class="QPushButton" name="btnClearAllOutputBindings">
1777+ <property name="text">
1778+ <string>Clear All</string>
1779+ </property>
1780+ </widget>
1781+ </item>
1782+ </layout>
1783+ </widget>
1784+ </item>
1785+ </layout>
1786+ </widget>
1787+ </widget>
1788+ </item>
1789+ <item row="0" column="2" rowspan="3">
1790+ <spacer name="horizontalSpacer">
1791+ <property name="orientation">
1792+ <enum>Qt::Horizontal</enum>
1793+ </property>
1794+ <property name="sizeHint" stdset="0">
1795+ <size>
1796+ <width>24</width>
1797+ <height>20</height>
1798+ </size>
1799+ </property>
1800+ </spacer>
1801+ </item>
1802+ <item row="0" column="0">
1803+ <widget class="QLabel" name="labelDeviceName">
1804+ <property name="font">
1805+ <font>
1806+ <pointsize>14</pointsize>
1807+ <weight>75</weight>
1808+ <bold>true</bold>
1809+ </font>
1810+ </property>
1811+ <property name="text">
1812+ <string>Your Device Name</string>
1813+ </property>
1814+ </widget>
1815+ </item>
1816+ <item row="1" column="0">
1817+ <widget class="QCheckBox" name="chkEnabledDevice">
1818+ <property name="text">
1819+ <string>Enabled</string>
1820+ </property>
1821+ </widget>
1822+ </item>
1823+ <item row="2" column="0">
1824+ <widget class="QLabel" name="label_3">
1825+ <property name="text">
1826+ <string>&lt;a href=&quot;http://mixxx.org/wiki/doku.php/midi_controller_mapping_file_format&quot;&gt;Troubleshooting&lt;/a&gt;</string>
1827+ </property>
1828+ <property name="alignment">
1829+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
1830+ </property>
1831+ <property name="openExternalLinks">
1832+ <bool>true</bool>
1833+ </property>
1834+ </widget>
1835+ </item>
1836+ </layout>
1837+ </widget>
1838+ <resources/>
1839+ <connections/>
1840+</ui>
1841
1842=== added file 'mixxx/src/dlgprefnomidi.cpp'
1843--- mixxx/src/dlgprefnomidi.cpp 1970-01-01 00:00:00 +0000
1844+++ mixxx/src/dlgprefnomidi.cpp 2012-06-27 11:06:21 +0000
1845@@ -0,0 +1,25 @@
1846+/***************************************************************************
1847+ dlgprefnomidi.cpp -
1848+***************************************************************************/
1849+
1850+/***************************************************************************
1851+* *
1852+* This program is free software; you can redistribute it and/or modify *
1853+* it under the terms of the GNU General Public License as published by *
1854+* the Free Software Foundation; either version 2 of the License, or *
1855+* (at your option) any later version. *
1856+* *
1857+***************************************************************************/
1858+
1859+#include "dlgprefnomidi.h"
1860+#include <QtCore>
1861+#include <QtGui>
1862+
1863+DlgPrefNoMidi::DlgPrefNoMidi(QWidget * parent, ConfigObject<ConfigValue> * _config) : QWidget(parent), Ui::DlgPrefNoMidiDlg()
1864+{
1865+ setupUi(this);
1866+}
1867+
1868+DlgPrefNoMidi::~DlgPrefNoMidi()
1869+{
1870+}
1871\ No newline at end of file
1872
1873=== added file 'mixxx/src/dlgprefnomidi.h'
1874--- mixxx/src/dlgprefnomidi.h 1970-01-01 00:00:00 +0000
1875+++ mixxx/src/dlgprefnomidi.h 2012-06-27 11:06:21 +0000
1876@@ -0,0 +1,33 @@
1877+/***************************************************************************
1878+ dlgprefnomidi.h - "No MIDI devices available"
1879+ -------------------
1880+ begin : Thu Apr 17 2003
1881+ copyright : (C) 2003 by Tue & Ken Haste Andersen
1882+ email : haste@diku.dk
1883+ ***************************************************************************/
1884+
1885+/***************************************************************************
1886+ * *
1887+ * This program is free software; you can redistribute it and/or modify *
1888+ * it under the terms of the GNU General Public License as published by *
1889+ * the Free Software Foundation; either version 2 of the License, or *
1890+ * (at your option) any later version. *
1891+ * *
1892+ ***************************************************************************/
1893+
1894+#ifndef DLGPREFNOMIDI_H
1895+#define DLGPREFNOMIDI_H
1896+
1897+#include "ui_dlgprefnomididlg.h"
1898+#include "configobject.h"
1899+
1900+//class QWidget;
1901+
1902+class DlgPrefNoMidi : public QWidget, public Ui::DlgPrefNoMidiDlg {
1903+ Q_OBJECT
1904+public:
1905+ DlgPrefNoMidi(QWidget *parent, ConfigObject<ConfigValue> *_config);
1906+ ~DlgPrefNoMidi();
1907+};
1908+
1909+#endif
1910
1911=== added file 'mixxx/src/dlgprefnomididlg.ui'
1912--- mixxx/src/dlgprefnomididlg.ui 1970-01-01 00:00:00 +0000
1913+++ mixxx/src/dlgprefnomididlg.ui 2012-06-27 11:06:21 +0000
1914@@ -0,0 +1,46 @@
1915+<ui version="4.0" >
1916+ <class>DlgPrefNoMidiDlg</class>
1917+ <widget class="QWidget" name="DlgPrefNoMidiDlg" >
1918+ <property name="geometry" >
1919+ <rect>
1920+ <x>0</x>
1921+ <y>0</y>
1922+ <width>462</width>
1923+ <height>87</height>
1924+ </rect>
1925+ </property>
1926+ <property name="windowTitle" >
1927+ <string>Form3</string>
1928+ </property>
1929+ <layout class="QVBoxLayout" >
1930+ <property name="alignment" >
1931+ <enum>Qt::AlignTop</enum>
1932+ </property>
1933+ <item>
1934+ <widget class="QLabel" name="TextLabel3_2" >
1935+ <property name="enabled" >
1936+ <bool>true</bool>
1937+ </property>
1938+ <property name="font" >
1939+ <font/>
1940+ </property>
1941+ <property name="minimumSize" >
1942+ <size>
1943+ <width>100</width>
1944+ <height>10</height>
1945+ </size>
1946+ </property>
1947+ <property name="text" >
1948+ <string>No MIDI devices available</string>
1949+ </property>
1950+ <property name="wordWrap" >
1951+ <bool>false</bool>
1952+ </property>
1953+ </widget>
1954+
1955+ </item>
1956+
1957+
1958+ </layout>
1959+ </widget>
1960+</ui>
1961
1962=== modified file 'mixxx/src/dlgprepare.cpp'
1963--- mixxx/src/dlgprepare.cpp 2012-04-29 05:43:31 +0000
1964+++ mixxx/src/dlgprepare.cpp 2012-06-27 11:06:21 +0000
1965@@ -92,6 +92,10 @@
1966 //m_pCratesTableModel->select();
1967 }
1968
1969+void DlgPrepare::onHide()
1970+{
1971+}
1972+
1973 void DlgPrepare::setup(QDomNode node)
1974 {
1975
1976
1977=== modified file 'mixxx/src/dlgprepare.h'
1978--- mixxx/src/dlgprepare.h 2012-06-25 00:02:38 +0000
1979+++ mixxx/src/dlgprepare.h 2012-06-27 11:06:21 +0000
1980@@ -27,6 +27,7 @@
1981 virtual void onSearchCleared();
1982 virtual void onSearch(const QString& text);
1983 virtual void onShow();
1984+ virtual void onHide();
1985 virtual void loadSelectedTrack();
1986 virtual void loadSelectedTrackToGroup(QString group);
1987 virtual void moveSelection(int delta);
1988
1989=== modified file 'mixxx/src/dlgrecording.cpp'
1990--- mixxx/src/dlgrecording.cpp 2012-06-25 00:02:38 +0000
1991+++ mixxx/src/dlgrecording.cpp 2012-06-27 11:06:21 +0000
1992@@ -70,6 +70,10 @@
1993 m_browseModel.setPath(m_recordingDir);
1994 }
1995
1996+void DlgRecording::onHide()
1997+{
1998+}
1999+
2000 void DlgRecording::setup(QDomNode node)
2001 {
2002
2003
2004=== modified file 'mixxx/src/dlgrecording.h'
2005--- mixxx/src/dlgrecording.h 2012-06-25 00:02:38 +0000
2006+++ mixxx/src/dlgrecording.h 2012-06-27 11:06:21 +0000
2007@@ -31,6 +31,7 @@
2008 virtual void onSearchCleared();
2009 virtual void onSearch(const QString& text);
2010 virtual void onShow();
2011+ virtual void onHide();
2012 virtual void loadSelectedTrack();
2013 virtual void loadSelectedTrackToGroup(QString group);
2014 virtual void moveSelection(int delta);
2015
2016=== added file 'mixxx/src/dlgselector.cpp'
2017--- mixxx/src/dlgselector.cpp 1970-01-01 00:00:00 +0000
2018+++ mixxx/src/dlgselector.cpp 2012-06-27 11:06:21 +0000
2019@@ -0,0 +1,196 @@
2020+#include <QSqlTableModel>
2021+#include "widget/wwidget.h"
2022+#include "widget/wskincolor.h"
2023+#include "library/selector/selectorlibrarytablemodel.h"
2024+#include "transposeproxymodel.h"
2025+#include "widget/wselectorlibrarytableview.h"
2026+#include "library/trackcollection.h"
2027+#include "dlgselector.h"
2028+
2029+
2030+DlgSelector::DlgSelector(QWidget* parent,
2031+ ConfigObject<ConfigValue>* pConfig,
2032+ TrackCollection* pTrackCollection)
2033+ : QWidget(parent), Ui::DlgSelector(),
2034+ m_pConfig(pConfig),
2035+ m_pTrackCollection(pTrackCollection) {
2036+ setupUi(this);
2037+
2038+ m_pSelectorLibraryTableView = new WSelectorLibraryTableView(this, pConfig, pTrackCollection,
2039+ ConfigKey(), ConfigKey());
2040+ connect(m_pSelectorLibraryTableView, SIGNAL(loadTrack(TrackPointer)),
2041+ this, SIGNAL(loadTrack(TrackPointer)));
2042+ connect(m_pSelectorLibraryTableView, SIGNAL(loadTrackToPlayer(TrackPointer, QString)),
2043+ this, SIGNAL(loadTrackToPlayer(TrackPointer, QString)));
2044+
2045+ QBoxLayout* box = dynamic_cast<QBoxLayout*>(layout());
2046+ Q_ASSERT(box); //Assumes the form layout is a QVBox/QHBoxLayout!
2047+ box->removeWidget(m_pTrackTablePlaceholder);
2048+ m_pTrackTablePlaceholder->hide();
2049+ box->insertWidget(1, m_pSelectorLibraryTableView);
2050+
2051+ m_pSelectorLibraryTableModel = new SelectorLibraryTableModel(this, pTrackCollection);
2052+ m_pSelectorLibraryTableView->loadTrackModel(m_pSelectorLibraryTableModel);
2053+
2054+ connect(checkBoxGenre, SIGNAL(clicked()),
2055+ this, SLOT(filterByGenre()));
2056+ connect(checkBoxBpm, SIGNAL(clicked()),
2057+ this, SLOT(filterByBpm()));
2058+ connect(checkBoxYear, SIGNAL(clicked()),
2059+ this, SLOT(filterByYear()));
2060+ connect(checkBoxRating, SIGNAL(clicked()),
2061+ this, SLOT(filterByRating()));
2062+ connect(checkBoxKey, SIGNAL(clicked()),
2063+ this, SLOT(filterByKey()));
2064+ connect(checkBoxKey4th, SIGNAL(clicked()),
2065+ this, SLOT(filterByKey4th()));
2066+ connect(checkBoxKey5th, SIGNAL(clicked()),
2067+ this, SLOT(filterByKey5th()));
2068+ connect(checkBoxKeyRelative, SIGNAL(clicked()),
2069+ this, SLOT(filterByKeyRelative()));
2070+ connect(horizontalSliderBpmRange, SIGNAL(valueChanged(int)),
2071+ this, SLOT(spinBoxBpmRangeChanged(int)));
2072+ connect(m_pSelectorLibraryTableView->selectionModel(),
2073+ SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection&)),
2074+ this, SLOT(tableSelectionChanged(const QItemSelection &, const QItemSelection&)));
2075+
2076+ connect(m_pSelectorLibraryTableModel,
2077+ SIGNAL(doSearch(const QItemSelection &, const QItemSelection&)),
2078+ this,
2079+ SLOT(tableSelectionChanged(const QItemSelection &, const QItemSelection&)));
2080+
2081+ connect(m_pSelectorLibraryTableModel, SIGNAL(filtersChanged()),
2082+ this, SLOT(slotFiltersChanged()));
2083+ // Getting info on current decks playing etc
2084+ connect(m_pSelectorLibraryTableModel, SIGNAL(currentTrackInfoChanged()),
2085+ this, SLOT(slotCurrentTrackInfoChanged()));
2086+
2087+
2088+}
2089+
2090+DlgSelector::~DlgSelector() {
2091+}
2092+
2093+void DlgSelector::onShow()
2094+{
2095+ qDebug() << "DlgSelector::onShow()";
2096+ m_pSelectorLibraryTableModel->active(true);
2097+ slotCurrentTrackInfoChanged();
2098+}
2099+
2100+void DlgSelector::onHide()
2101+{
2102+ qDebug() << "DlgSelector::onHide()";
2103+ m_pSelectorLibraryTableModel->active(false);
2104+}
2105+
2106+void DlgSelector::setup(QDomNode node)
2107+{
2108+ qDebug() << "DlgSelector::setup()";
2109+}
2110+
2111+void DlgSelector::onSearchStarting()
2112+{
2113+}
2114+
2115+void DlgSelector::onSearchCleared()
2116+{
2117+}
2118+
2119+void DlgSelector::slotFiltersChanged() {
2120+ int count = m_pSelectorLibraryTableModel->rowCount();
2121+ QString pluralize = ((count > 1) ? QString("s") : QString(""));
2122+ QString labelMatchText = QString(tr("%1 Track%2 Found ")).arg(count).arg(pluralize);
2123+ labelMatchCount->setText(labelMatchText);
2124+}
2125+
2126+void DlgSelector::slotCurrentTrackInfoChanged() {
2127+ // check which filters to activate
2128+ checkBoxGenre->setEnabled(m_pSelectorLibraryTableModel->currentTrackGenreExists());
2129+ checkBoxBpm->setEnabled(m_pSelectorLibraryTableModel->currentTrackBpmExists());
2130+ horizontalSliderBpmRange->setEnabled(m_pSelectorLibraryTableModel->currentTrackBpmExists());
2131+ checkBoxYear->setEnabled(m_pSelectorLibraryTableModel->currentTrackYearExists());
2132+ checkBoxRating->setEnabled(m_pSelectorLibraryTableModel->currentTrackRatingExists());
2133+ checkBoxKey->setEnabled(m_pSelectorLibraryTableModel->currentTrackKeyExists());
2134+ checkBoxKey4th->setEnabled(m_pSelectorLibraryTableModel->currentTrackKeyExists());
2135+ checkBoxKey5th->setEnabled(m_pSelectorLibraryTableModel->currentTrackKeyExists());
2136+ checkBoxKeyRelative->setEnabled(m_pSelectorLibraryTableModel->currentTrackKeyExists());
2137+}
2138+
2139+void DlgSelector::onSearch(const QString& text)
2140+{
2141+ m_pSelectorLibraryTableModel->search(text);
2142+}
2143+
2144+void DlgSelector::loadSelectedTrack() {
2145+ m_pSelectorLibraryTableView->loadSelectedTrack();
2146+}
2147+
2148+void DlgSelector::loadSelectedTrackToGroup(QString group) {
2149+ m_pSelectorLibraryTableView->loadSelectedTrackToGroup(group);
2150+}
2151+
2152+void DlgSelector::moveSelection(int delta) {
2153+ m_pSelectorLibraryTableView->moveSelection(delta);
2154+}
2155+
2156+void DlgSelector::tableSelectionChanged(const QItemSelection& selected,
2157+ const QItemSelection& deselected)
2158+{
2159+}
2160+
2161+void DlgSelector::selectAll() {
2162+ m_pSelectorLibraryTableView->selectAll();
2163+}
2164+
2165+void DlgSelector::filterByGenre()
2166+{
2167+ m_pSelectorLibraryTableModel->filterByGenre(checkBoxGenre->isChecked());
2168+}
2169+
2170+void DlgSelector::filterByBpm()
2171+{
2172+ bool bpm = checkBoxBpm->isChecked();
2173+ int range = horizontalSliderBpmRange->value();
2174+ m_pSelectorLibraryTableModel->filterByBpm(bpm, range);
2175+}
2176+
2177+void DlgSelector::spinBoxBpmRangeChanged(int value)
2178+{
2179+ filterByBpm();
2180+}
2181+
2182+void DlgSelector::filterByYear()
2183+{
2184+ m_pSelectorLibraryTableModel->filterByYear(checkBoxYear->isChecked());
2185+}
2186+
2187+void DlgSelector::filterByRating()
2188+{
2189+ m_pSelectorLibraryTableModel->filterByRating(checkBoxRating->isChecked());
2190+}
2191+
2192+void DlgSelector::filterByKey()
2193+{
2194+ m_pSelectorLibraryTableModel->filterByKey(checkBoxKey->isChecked());
2195+}
2196+
2197+void DlgSelector::filterByKey4th()
2198+{
2199+ m_pSelectorLibraryTableModel->filterByKey4th(checkBoxKey4th->isChecked());
2200+}
2201+
2202+void DlgSelector::filterByKey5th()
2203+{
2204+ m_pSelectorLibraryTableModel->filterByKey5th(checkBoxKey5th->isChecked());
2205+}
2206+
2207+void DlgSelector::filterByKeyRelative()
2208+{
2209+ m_pSelectorLibraryTableModel->filterByKeyRelative(checkBoxKeyRelative->isChecked());
2210+}
2211+
2212+void DlgSelector::installEventFilter(QObject* pFilter) {
2213+ QWidget::installEventFilter(pFilter);
2214+ m_pSelectorLibraryTableView->installEventFilter(pFilter);
2215+}
2216
2217=== added file 'mixxx/src/dlgselector.h'
2218--- mixxx/src/dlgselector.h 1970-01-01 00:00:00 +0000
2219+++ mixxx/src/dlgselector.h 2012-06-27 11:06:21 +0000
2220@@ -0,0 +1,67 @@
2221+#ifndef DLGSELECTOR_H
2222+#define DLGSELECTOR_H
2223+
2224+#include <QItemSelection>
2225+#include "ui_dlgselector.h"
2226+#include "configobject.h"
2227+#include "library/libraryview.h"
2228+#include "library/trackcollection.h"
2229+
2230+class SelectorLibraryTableModel;
2231+class WSelectorCratesTableView;
2232+class WSelectorLibraryTableView;
2233+class QSqlTableModel;
2234+class CrateView;
2235+
2236+class DlgSelector : public QWidget, public Ui::DlgSelector, public virtual LibraryView {
2237+ Q_OBJECT
2238+ public:
2239+ DlgSelector(QWidget *parent,
2240+ ConfigObject<ConfigValue>* pConfig,
2241+ TrackCollection* pTrackCollection);
2242+ virtual ~DlgSelector();
2243+
2244+ virtual void setup(QDomNode node);
2245+ virtual void onSearchStarting();
2246+ virtual void onSearchCleared();
2247+ virtual void onSearch(const QString& text);
2248+ virtual void onShow();
2249+ virtual void onHide();
2250+ virtual void loadSelectedTrack();
2251+ virtual void loadSelectedTrackToGroup(QString group);
2252+ virtual void moveSelection(int delta);
2253+
2254+ public slots:
2255+ void tableSelectionChanged(const QItemSelection& selected,
2256+ const QItemSelection& deselected);
2257+ void selectAll();
2258+ void filterByGenre();
2259+ void filterByBpm();
2260+ void spinBoxBpmRangeChanged(int value);
2261+ void filterByYear();
2262+ void filterByRating();
2263+ void filterByKey();
2264+ void filterByKey4th();
2265+ void filterByKey5th();
2266+ void filterByKeyRelative();
2267+ void installEventFilter(QObject* pFilter);
2268+ void slotFiltersChanged();
2269+ void slotCurrentTrackInfoChanged();
2270+
2271+ signals:
2272+ void loadTrack(TrackPointer pTrack);
2273+ void loadTrackToPlayer(TrackPointer pTrack, QString player);
2274+
2275+ private:
2276+ //Note m_pTrackTablePlaceholder is defined in the .ui file
2277+ ConfigObject<ConfigValue>* m_pConfig;
2278+ TrackCollection* m_pTrackCollection;
2279+ //QButtonGroup m_songsButtonGroup;
2280+ WSelectorLibraryTableView* m_pSelectorLibraryTableView;
2281+ SelectorLibraryTableModel* m_pSelectorLibraryTableModel;
2282+ WSelectorCratesTableView* m_pSelectorCratesTableView;
2283+ CrateView* m_pCrateView;
2284+ QSqlTableModel* m_pCratesTableModel;
2285+};
2286+
2287+#endif //DLGSELECTOR_H
2288
2289=== added file 'mixxx/src/dlgselector.ui'
2290--- mixxx/src/dlgselector.ui 1970-01-01 00:00:00 +0000
2291+++ mixxx/src/dlgselector.ui 2012-06-27 11:06:21 +0000
2292@@ -0,0 +1,155 @@
2293+<?xml version="1.0" encoding="UTF-8"?>
2294+<ui version="4.0">
2295+ <class>DlgSelector</class>
2296+ <widget class="QWidget" name="DlgSelector">
2297+ <property name="geometry">
2298+ <rect>
2299+ <x>0</x>
2300+ <y>0</y>
2301+ <width>619</width>
2302+ <height>399</height>
2303+ </rect>
2304+ </property>
2305+ <property name="windowTitle">
2306+ <string>Manage</string>
2307+ </property>
2308+ <layout class="QVBoxLayout" name="verticalLayout">
2309+ <property name="leftMargin">
2310+ <number>0</number>
2311+ </property>
2312+ <property name="rightMargin">
2313+ <number>0</number>
2314+ </property>
2315+ <property name="bottomMargin">
2316+ <number>0</number>
2317+ </property>
2318+ <item>
2319+ <layout class="QHBoxLayout" name="horizontalLayout">
2320+ <item>
2321+ <spacer name="horizontalSpacer_3">
2322+ <property name="orientation">
2323+ <enum>Qt::Horizontal</enum>
2324+ </property>
2325+ <property name="sizeType">
2326+ <enum>QSizePolicy::Fixed</enum>
2327+ </property>
2328+ <property name="sizeHint" stdset="0">
2329+ <size>
2330+ <width>12</width>
2331+ <height>20</height>
2332+ </size>
2333+ </property>
2334+ </spacer>
2335+ </item>
2336+ <item>
2337+ <widget class="QCheckBox" name="checkBoxGenre">
2338+ <property name="text">
2339+ <string>Genre</string>
2340+ </property>
2341+ </widget>
2342+ </item>
2343+ <item>
2344+ <widget class="QCheckBox" name="checkBoxBpm">
2345+ <property name="text">
2346+ <string>BPM</string>
2347+ </property>
2348+ </widget>
2349+ </item>
2350+ <item>
2351+ <widget class="QSlider" name="horizontalSliderBpmRange">
2352+ <property name="toolTip">
2353+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Bpm filter range&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
2354+ </property>
2355+ <property name="maximum">
2356+ <number>8</number>
2357+ </property>
2358+ <property name="pageStep">
2359+ <number>1</number>
2360+ </property>
2361+ <property name="orientation">
2362+ <enum>Qt::Horizontal</enum>
2363+ </property>
2364+ <property name="tickPosition">
2365+ <enum>QSlider::TicksBelow</enum>
2366+ </property>
2367+ <property name="tickInterval">
2368+ <number>1</number>
2369+ </property>
2370+ </widget>
2371+ </item>
2372+ <item>
2373+ <widget class="QCheckBox" name="checkBoxYear">
2374+ <property name="text">
2375+ <string>Year</string>
2376+ </property>
2377+ </widget>
2378+ </item>
2379+ <item>
2380+ <widget class="QCheckBox" name="checkBoxRating">
2381+ <property name="text">
2382+ <string>Rating</string>
2383+ </property>
2384+ </widget>
2385+ </item>
2386+ <item>
2387+ <widget class="QCheckBox" name="checkBoxKey">
2388+ <property name="text">
2389+ <string>Key</string>
2390+ </property>
2391+ </widget>
2392+ </item>
2393+ <item>
2394+ <widget class="QCheckBox" name="checkBoxKey4th">
2395+ <property name="text">
2396+ <string>Perfect 4th</string>
2397+ </property>
2398+ </widget>
2399+ </item>
2400+ <item>
2401+ <widget class="QCheckBox" name="checkBoxKey5th">
2402+ <property name="text">
2403+ <string>Perfect 5th</string>
2404+ </property>
2405+ </widget>
2406+ </item>
2407+ <item>
2408+ <widget class="QCheckBox" name="checkBoxKeyRelative">
2409+ <property name="text">
2410+ <string>Relative Key</string>
2411+ </property>
2412+ </widget>
2413+ </item>
2414+ <item>
2415+ <spacer name="horizontalSpacer">
2416+ <property name="orientation">
2417+ <enum>Qt::Horizontal</enum>
2418+ </property>
2419+ <property name="sizeHint" stdset="0">
2420+ <size>
2421+ <width>40</width>
2422+ <height>20</height>
2423+ </size>
2424+ </property>
2425+ </spacer>
2426+ </item>
2427+ <item>
2428+ <widget class="QLabel" name="labelMatchCount">
2429+ <property name="text">
2430+ <string>Tracks</string>
2431+ </property>
2432+ </widget>
2433+ </item>
2434+ </layout>
2435+ </item>
2436+ <item>
2437+ <widget class="QTableView" name="m_pTrackTablePlaceholder">
2438+ <property name="showGrid">
2439+ <bool>true</bool>
2440+ </property>
2441+ </widget>
2442+ </item>
2443+ </layout>
2444+ </widget>
2445+ <resources/>
2446+ <connections/>
2447+</ui>
2448
2449=== modified file 'mixxx/src/library/bundledsongswebview.cpp'
2450--- mixxx/src/library/bundledsongswebview.cpp 2011-03-22 16:24:04 +0000
2451+++ mixxx/src/library/bundledsongswebview.cpp 2012-06-27 11:06:21 +0000
2452@@ -96,6 +96,10 @@
2453 // page()->mainFrame()->evaluateJavaScript("showMainStuff(0, 0);");
2454 }
2455
2456+void BundledSongsWebView::onHide()
2457+{
2458+}
2459+
2460 /* Google Analytics doesn't like our crappy malformed "Mixxx 1.8" string
2461 as a user agent. Let Qt construct it for us instead by leaving this commented out.
2462 QString PromoTracksWebView::userAgentForUrl (const QUrl & url) const
2463
2464=== modified file 'mixxx/src/library/bundledsongswebview.h'
2465--- mixxx/src/library/bundledsongswebview.h 2011-03-22 16:24:04 +0000
2466+++ mixxx/src/library/bundledsongswebview.h 2012-06-27 11:06:21 +0000
2467@@ -45,6 +45,7 @@
2468 virtual void onSearchCleared() {};
2469 virtual void onSearch(const QString&) {};
2470 virtual void onShow();
2471+ virtual void onHide();
2472 virtual void keyPressEvent(QKeyEvent* event);
2473 virtual void loadSelectedTrack();
2474 virtual void loadSelectedTrackToGroup(QString group);
2475
2476=== modified file 'mixxx/src/library/featuredartistswebview.h'
2477--- mixxx/src/library/featuredartistswebview.h 2010-10-24 09:50:11 +0000
2478+++ mixxx/src/library/featuredartistswebview.h 2012-06-27 11:06:21 +0000
2479@@ -37,6 +37,7 @@
2480 virtual void onSearchCleared() {};
2481 virtual void onSearch(const QString&) {};
2482 virtual void onShow() {};
2483+ virtual void onHide() {};
2484 virtual QWidget* getWidgetForMIDIControl() { return this; };
2485 virtual void keyPressEvent(QKeyEvent* event);
2486
2487
2488=== modified file 'mixxx/src/library/library.cpp'
2489--- mixxx/src/library/library.cpp 2012-05-11 05:42:47 +0000
2490+++ mixxx/src/library/library.cpp 2012-06-27 11:06:21 +0000
2491@@ -18,6 +18,7 @@
2492 #include "library/autodjfeature.h"
2493 #include "library/playlistfeature.h"
2494 #include "library/preparefeature.h"
2495+#include "library/selector/selectorfeature.h"
2496 #include "library/promotracksfeature.h"
2497 #include "library/traktor/traktorfeature.h"
2498 #include "library/librarycontrol.h"
2499@@ -63,6 +64,7 @@
2500 addFeature(new RecordingFeature(this, pConfig, m_pTrackCollection, m_pRecordingManager));
2501 addFeature(new SetlogFeature(this, pConfig, m_pTrackCollection));
2502 addFeature(new PrepareFeature(this, pConfig, m_pTrackCollection));
2503+ addFeature(new SelectorFeature(this, pConfig, m_pTrackCollection));
2504 //iTunes and Rhythmbox should be last until we no longer have an obnoxious
2505 //messagebox popup when you select them. (This forces you to reach for your
2506 //mouse or keyboard if you're using MIDI control and you scroll through them...)
2507
2508=== modified file 'mixxx/src/library/libraryview.h'
2509--- mixxx/src/library/libraryview.h 2011-03-22 16:24:04 +0000
2510+++ mixxx/src/library/libraryview.h 2012-06-27 11:06:21 +0000
2511@@ -17,6 +17,7 @@
2512 virtual void onSearchCleared() = 0;
2513 virtual void onSearch(const QString& text) = 0;
2514 virtual void onShow() = 0;
2515+ virtual void onHide() = 0;
2516
2517 // If applicable, requests that the LibraryView load the selected
2518 // track. Does nothing otherwise.
2519
2520=== added directory 'mixxx/src/library/selector'
2521=== added file 'mixxx/src/library/selector/selectorfeature.cpp'
2522--- mixxx/src/library/selector/selectorfeature.cpp 1970-01-01 00:00:00 +0000
2523+++ mixxx/src/library/selector/selectorfeature.cpp 2012-06-27 11:06:21 +0000
2524@@ -0,0 +1,92 @@
2525+// selectorfeature.cpp
2526+// Created 3/17/2012 by Keith Salisbury (keithsalisbury@gmail.com)
2527+
2528+#include <QtDebug>
2529+
2530+#include "library/selector/selectorfeature.h"
2531+#include "library/librarytablemodel.h"
2532+#include "library/trackcollection.h"
2533+#include "dlgselector.h"
2534+#include "widget/wlibrary.h"
2535+#include "widget/wlibrarysidebar.h"
2536+#include "mixxxkeyboard.h"
2537+
2538+const QString SelectorFeature::m_sSelectorViewName = QString("Selector");
2539+
2540+SelectorFeature::SelectorFeature(QObject* parent,
2541+ ConfigObject<ConfigValue>* pConfig,
2542+ TrackCollection* pTrackCollection)
2543+ : LibraryFeature(parent),
2544+ m_pConfig(pConfig),
2545+ m_pTrackCollection(pTrackCollection) {
2546+}
2547+
2548+SelectorFeature::~SelectorFeature() {
2549+ // TODO(XXX) delete these
2550+ //delete m_pLibraryTableModel;
2551+}
2552+
2553+QVariant SelectorFeature::title() {
2554+ return tr("Selector");
2555+}
2556+
2557+QIcon SelectorFeature::getIcon() {
2558+ return QIcon(":/images/library/ic_library_selector.png");
2559+}
2560+
2561+void SelectorFeature::bindWidget(WLibrarySidebar* sidebarWidget,
2562+ WLibrary* libraryWidget,
2563+ MixxxKeyboard* keyboard) {
2564+ DlgSelector* pSelectorView = new DlgSelector(libraryWidget,
2565+ m_pConfig,
2566+ m_pTrackCollection);
2567+ connect(pSelectorView, SIGNAL(loadTrack(TrackPointer)),
2568+ this, SIGNAL(loadTrack(TrackPointer)));
2569+ connect(pSelectorView, SIGNAL(loadTrackToPlayer(TrackPointer, QString)),
2570+ this, SIGNAL(loadTrackToPlayer(TrackPointer, QString)));
2571+
2572+ pSelectorView->installEventFilter(keyboard);
2573+
2574+ libraryWidget->registerView(m_sSelectorViewName, pSelectorView);
2575+}
2576+
2577+TreeItemModel* SelectorFeature::getChildModel() {
2578+ return &m_childModel;
2579+}
2580+
2581+void SelectorFeature::activate() {
2582+ //qDebug() << "SelectorFeature::activate()";
2583+ emit(switchToView(m_sSelectorViewName));
2584+}
2585+
2586+void SelectorFeature::activateChild(const QModelIndex& index) {
2587+}
2588+
2589+void SelectorFeature::onRightClick(const QPoint& globalPos) {
2590+}
2591+
2592+void SelectorFeature::onRightClickChild(const QPoint& globalPos,
2593+ QModelIndex index) {
2594+}
2595+
2596+bool SelectorFeature::dropAccept(QUrl url) {
2597+ return false;
2598+}
2599+
2600+bool SelectorFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {
2601+ return false;
2602+}
2603+
2604+bool SelectorFeature::dragMoveAccept(QUrl url) {
2605+ return false;
2606+}
2607+
2608+bool SelectorFeature::dragMoveAcceptChild(const QModelIndex& index,
2609+ QUrl url) {
2610+ return false;
2611+}
2612+
2613+void SelectorFeature::onLazyChildExpandation(const QModelIndex &index){
2614+ //Nothing to do because the childmodel is not of lazy nature.
2615+}
2616+
2617
2618=== added file 'mixxx/src/library/selector/selectorfeature.h'
2619--- mixxx/src/library/selector/selectorfeature.h 1970-01-01 00:00:00 +0000
2620+++ mixxx/src/library/selector/selectorfeature.h 2012-06-27 11:06:21 +0000
2621@@ -0,0 +1,58 @@
2622+// selectorfeature.h
2623+// Created 8/23/2012 by Keith Salisbury (keith@globalkeith.com)
2624+
2625+#ifndef SELECTORFEATURE_H
2626+#define SELECTORFEATURE_H
2627+
2628+#include <QStringListModel>
2629+#include "library/libraryfeature.h"
2630+#include "library/treeitemmodel.h"
2631+#include "configobject.h"
2632+
2633+class LibraryTableModel;
2634+class TrackCollection;
2635+
2636+class SelectorFeature : public LibraryFeature {
2637+ Q_OBJECT
2638+ public:
2639+ SelectorFeature(QObject* parent,
2640+ ConfigObject<ConfigValue>* pConfig,
2641+ TrackCollection* pTrackCollection);
2642+ virtual ~SelectorFeature();
2643+
2644+ QVariant title();
2645+ QIcon getIcon();
2646+
2647+ bool dropAccept(QUrl url);
2648+ bool dropAcceptChild(const QModelIndex& index, QUrl url);
2649+ bool dragMoveAccept(QUrl url);
2650+ bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
2651+
2652+ void bindWidget(WLibrarySidebar* sidebarWidget,
2653+ WLibrary* libraryWidget,
2654+ MixxxKeyboard* keyboard);
2655+
2656+ TreeItemModel* getChildModel();
2657+
2658+ signals:
2659+
2660+ public slots:
2661+ void activate();
2662+ void activateChild(const QModelIndex& index);
2663+ void onRightClick(const QPoint& globalPos);
2664+ void onRightClickChild(const QPoint& globalPos, QModelIndex index);
2665+ void onLazyChildExpandation(const QModelIndex& index);
2666+
2667+ private slots:
2668+
2669+ private:
2670+ ConfigObject<ConfigValue>* m_pConfig;
2671+ TrackCollection* m_pTrackCollection;
2672+ // Used to temporarily enable BPM detection in the prefs before we analyse
2673+ int m_iOldBpmEnabled;
2674+ TreeItemModel m_childModel;
2675+ const static QString m_sSelectorViewName;
2676+};
2677+
2678+
2679+#endif /* SELECTORFEATURE_H */
2680
2681=== added file 'mixxx/src/library/selector/selectorlibrarytablemodel.cpp'
2682--- mixxx/src/library/selector/selectorlibrarytablemodel.cpp 1970-01-01 00:00:00 +0000
2683+++ mixxx/src/library/selector/selectorlibrarytablemodel.cpp 2012-06-27 11:06:21 +0000
2684@@ -0,0 +1,390 @@
2685+// Created 3/17/2012 by Keith Salisbury (keithsalisbury@gmail.com)
2686+
2687+#include <QObject>
2688+
2689+#include "selectorlibrarytablemodel.h"
2690+#include "library/trackcollection.h"
2691+#include "basetrackplayer.h"
2692+#include "playerinfo.h"
2693+
2694+#include "controlobjectthreadmain.h"
2695+#include "controlobject.h"
2696+#include "trackinfoobject.h"
2697+
2698+const bool sDebug = true;
2699+
2700+SelectorLibraryTableModel::SelectorLibraryTableModel(QObject* parent,
2701+ TrackCollection* pTrackCollection)
2702+ : LibraryTableModel(parent, pTrackCollection,
2703+ "mixxx.db.model.selector") {
2704+
2705+ connect(this, SIGNAL(doSearch(const QString&)),
2706+ this, SLOT(slotSearch(const QString&)));
2707+
2708+ // Getting info on current decks playing etc
2709+ connect(&PlayerInfo::Instance(), SIGNAL(currentPlayingDeckChanged(int)),
2710+ this, SLOT(slotPlayingDeckChanged(int)));
2711+
2712+ connect(this, SIGNAL(filtersChanged()),
2713+ this, SLOT(slotFiltersChanged()));
2714+
2715+ m_bFilterGenre = false;
2716+ m_bFilterBpm = false;
2717+ m_iFilterBpmRange = 0;
2718+ m_bFilterYear = false;
2719+ m_bFilterRating = false;
2720+ m_bFilterKey = false;
2721+ m_bFilterKey4th = false;
2722+ m_bFilterKey5th = false;
2723+ m_bFilterKeyRelative = false;
2724+ m_filterString = "";
2725+ m_channelBpm = NULL;
2726+ m_bActive = false;
2727+ m_rate = 0;
2728+
2729+ m_sCurrentTrackGenre = "";
2730+ m_fCurrentTrackBpm = 0;
2731+ m_sCurrentTrackYear = "";
2732+ m_iCurrentTrackRating = 0;
2733+ m_sCurrentTrackKey = "";
2734+
2735+ initializeHarmonicsData();
2736+
2737+}
2738+
2739+SelectorLibraryTableModel::~SelectorLibraryTableModel() {
2740+}
2741+
2742+bool SelectorLibraryTableModel::isColumnInternal(int column) {
2743+ return LibraryTableModel::isColumnInternal(column);
2744+}
2745+
2746+int SelectorLibraryTableModel::rowCount() {
2747+ return BaseSqlTableModel::rowCount();
2748+}
2749+
2750+void SelectorLibraryTableModel::active(bool value) {
2751+ m_bActive = value;
2752+}
2753+
2754+bool SelectorLibraryTableModel::currentTrackGenreExists() {
2755+ return m_sCurrentTrackGenre != QString();
2756+}
2757+
2758+bool SelectorLibraryTableModel::currentTrackBpmExists() {
2759+ return m_fCurrentTrackBpm > 0;
2760+}
2761+
2762+bool SelectorLibraryTableModel::currentTrackYearExists() {
2763+ return m_sCurrentTrackYear != QString();
2764+}
2765+
2766+bool SelectorLibraryTableModel::currentTrackRatingExists() {
2767+ return m_iCurrentTrackRating > 0;
2768+}
2769+
2770+bool SelectorLibraryTableModel::currentTrackKeyExists() {
2771+ return m_sCurrentTrackKey != QString();
2772+}
2773+
2774+void SelectorLibraryTableModel::filterByGenre(bool value) {
2775+ m_bFilterGenre = value;
2776+ updateFilterText();
2777+}
2778+
2779+void SelectorLibraryTableModel::filterByBpm(bool value, int range) {
2780+ m_bFilterBpm = value;
2781+ m_iFilterBpmRange = range;
2782+ updateFilterText();
2783+}
2784+
2785+void SelectorLibraryTableModel::filterByYear(bool value) {
2786+ m_bFilterYear = value;
2787+ updateFilterText();
2788+}
2789+
2790+void SelectorLibraryTableModel::filterByRating(bool value) {
2791+ m_bFilterRating = value;
2792+ updateFilterText();
2793+}
2794+
2795+void SelectorLibraryTableModel::filterByKey(bool value) {
2796+ m_bFilterKey = value;
2797+ updateFilterText();
2798+}
2799+
2800+void SelectorLibraryTableModel::filterByKey4th(bool value) {
2801+ m_bFilterKey4th = value;
2802+ updateFilterText();
2803+}
2804+
2805+void SelectorLibraryTableModel::filterByKey5th(bool value) {
2806+ m_bFilterKey5th = value;
2807+ updateFilterText();
2808+}
2809+
2810+void SelectorLibraryTableModel::filterByKeyRelative(bool value) {
2811+ m_bFilterKeyRelative = value;
2812+ updateFilterText();
2813+}
2814+
2815+// PRIVATE SLOTS
2816+
2817+void SelectorLibraryTableModel::slotPlayingDeckChanged(int deck) {
2818+ m_pChannel = QString("[Channel%1]").arg(deck);
2819+
2820+ // disconnect the old pitch slider
2821+ if (m_channelBpm) {
2822+ m_channelBpm->disconnect(this);
2823+ }
2824+ // get the new pitch slider object
2825+ m_channelBpm = new ControlObjectThreadMain(
2826+ ControlObject::getControl(ConfigKey(m_pChannel, "bpm")));
2827+ // listen for slider change events
2828+ connect(m_channelBpm, SIGNAL(valueChanged(double)), this,
2829+ SLOT(slotChannel1BpmChanged(double)));
2830+
2831+ m_pLoadedTrack = PlayerInfo::Instance().getTrackInfo(m_pChannel);
2832+
2833+ m_sCurrentTrackGenre = m_pLoadedTrack->getGenre();
2834+ m_fCurrentTrackBpm = m_pLoadedTrack->getBpm();
2835+ m_sCurrentTrackYear = m_pLoadedTrack->getYear();
2836+ m_iCurrentTrackRating = m_pLoadedTrack->getRating();
2837+ m_sCurrentTrackKey = m_pLoadedTrack->getKey();
2838+
2839+ emit(currentTrackInfoChanged());
2840+
2841+ setRate();
2842+ updateFilterText();
2843+}
2844+
2845+
2846+void SelectorLibraryTableModel::slotChannel1BpmChanged(double value) {
2847+ #define DISCARD_PARAMETER(p) (void)p
2848+ (void)value;
2849+ setRate();
2850+ updateFilterText();
2851+}
2852+
2853+void SelectorLibraryTableModel::slotFiltersChanged() {
2854+ if (!m_bActive) return;
2855+ slotSearch(QString());
2856+}
2857+
2858+void SelectorLibraryTableModel::slotSearch(const QString& searchText) {
2859+ BaseSqlTableModel::search(searchText, m_filterString);
2860+}
2861+
2862+// PRIVATE METHODS
2863+
2864+void SelectorLibraryTableModel::updateFilterText() {
2865+ if (!m_bActive) return;
2866+ if (m_pLoadedTrack) {
2867+ QStringList filters;
2868+
2869+ // Genre
2870+ if (m_bFilterGenre) {
2871+ QString TrackGenre = m_sCurrentTrackGenre;
2872+ if (TrackGenre != "")
2873+ filters << QString("Genre == '%1'").arg(TrackGenre);
2874+ }
2875+
2876+ // Year
2877+ if (m_bFilterYear) {
2878+ QString TrackYear = m_sCurrentTrackYear;
2879+ if (TrackYear!="")
2880+ filters << QString("Year == '%1'").arg(TrackYear);
2881+ }
2882+
2883+ // Rating
2884+ if (m_bFilterRating) {
2885+ int TrackRating = m_iCurrentTrackRating;
2886+ if (TrackRating > 0)
2887+ filters << QString("Rating >= %1").arg(TrackRating);
2888+ }
2889+
2890+ // calculate the current BPM
2891+ float trackBpm = m_fCurrentTrackBpm;
2892+ float currentBpm = trackBpm * m_rate;
2893+
2894+ // Bpm
2895+ if (m_bFilterBpm) {
2896+ //float trackBpm = pChannel1Bpm->get();
2897+ if (currentBpm > 0)
2898+ filters << QString("(Bpm > %1 AND Bpm < %2)").arg(
2899+ floor(currentBpm - m_iFilterBpmRange)).arg(
2900+ ceil(currentBpm + m_iFilterBpmRange));
2901+ }
2902+
2903+ // Keys
2904+
2905+ // calculate the new pitch
2906+ const int semitonesPerOctave = 12;
2907+ float frequencyRatio = currentBpm / trackBpm;
2908+ float semitoneOffset = semitonesPerOctave * log(frequencyRatio) / log(2);
2909+ QString trackKey = adjustPitchBy(m_sCurrentTrackKey, semitoneOffset);
2910+
2911+ QString hKeys = getHarmonicKeys(trackKey);
2912+ if (hKeys!="")
2913+ filters << QString("Key in (%1)").arg(hKeys);
2914+
2915+ QString filterString = filters.join(" AND ");
2916+ if (m_filterString != filterString) {
2917+ m_filterString = filterString;
2918+ emit(filtersChanged());
2919+ }
2920+
2921+ }
2922+
2923+}
2924+
2925+void SelectorLibraryTableModel::setRate() {
2926+ // get pitch slider value (deck rate)
2927+ ControlObjectThreadMain* rateSlider = new ControlObjectThreadMain(
2928+ ControlObject::getControl(ConfigKey(m_pChannel, "rate")));
2929+ ControlObjectThreadMain* rateRange = new ControlObjectThreadMain(
2930+ ControlObject::getControl(ConfigKey(m_pChannel, "rateRange")));
2931+ ControlObjectThreadMain* rateDirection = new ControlObjectThreadMain(
2932+ ControlObject::getControl(ConfigKey(m_pChannel, "rate_dir")));
2933+
2934+ if (rateSlider != NULL && rateRange != NULL && rateDirection != NULL) {
2935+ m_rate = (1 + rateSlider->get() * rateRange->get() * rateDirection->get());
2936+ }
2937+}
2938+
2939+void SelectorLibraryTableModel::search(const QString& searchText) {
2940+ emit(doSearch(searchText));
2941+}
2942+
2943+QString SelectorLibraryTableModel::getHarmonicKeys(QString trackKey) const {
2944+ // determine major or minor key "m"
2945+ bool isMinor = trackKey.contains("m", Qt::CaseInsensitive);
2946+
2947+ QStringList keys1 = (isMinor ? m_minors : m_majors);
2948+ QStringList keys2 = (isMinor ? m_majors : m_minors);
2949+
2950+ int index = keys1.indexOf(trackKey);
2951+ int len = keys1.count();
2952+ if (index < 0) return QString("");
2953+ int lower = index-1;
2954+ if (lower < 0) lower += len;
2955+ int upper = index+1;
2956+ if (upper >= len) upper -= len;
2957+
2958+ QStringList keyfilters;
2959+ // Tonic Key
2960+ if (m_bFilterKey)
2961+ keyfilters << QString("'%1'").arg(trackKey);
2962+ // Perfect Fourth (Sub Dominant)
2963+ if (m_bFilterKey4th)
2964+ keyfilters << QString("'%1'").arg(keys1[lower]);
2965+ // Perfect Fifth (Dominant)
2966+ if (m_bFilterKey5th)
2967+ keyfilters << QString("'%1'").arg(keys1[upper]);
2968+ // Relative Minor/Major
2969+ if (m_bFilterKeyRelative)
2970+ keyfilters << QString("'%1'").arg(keys2[index]);
2971+
2972+ return keyfilters.join(",");
2973+}
2974+
2975+QString SelectorLibraryTableModel::adjustPitchBy(QString pitch, int change) {
2976+ if (pitch == "") return pitch;
2977+
2978+ int position = m_semitoneList.indexOf(pitch);
2979+ if (position<0)
2980+ return pitch;
2981+
2982+ int newpos = position + (change * 2);
2983+ if (newpos >= 24)
2984+ newpos = newpos - 24;
2985+
2986+ QString newpitch = m_semitoneList.at(newpos);
2987+ return newpitch;
2988+}
2989+
2990+void SelectorLibraryTableModel::initializeHarmonicsData() {
2991+ /*
2992+ m_enharmonic_preference = "b" // # or b
2993+ "A# = Bb"
2994+ "C# = Db"
2995+ "D# = Eb"
2996+ "F# = Gb"
2997+ "G# = Ab"
2998+
2999+ m_enharmonic_preference = "#" // # or b
3000+ "Ab = G#"
3001+ "Bb = A#"
3002+ "Db = C#"
3003+ "Eb = D#"
3004+ "Gb = F#"
3005+
3006+ m_majors = QString("C,G,D,A,E,B,F#,Db,Ab,Eb,Bb,F").split(",");
3007+ m_minors = QString("Am,Em,Bm,F#m,Dbm,Abm,Ebm,Bbm,Fm,Cm,Gm,Dm").split(",");
3008+ m_semitoneList = QString("C,Cm,C#,C#m,D,Dm,Eb,Ebm,E,Em,F,Fm,F#,F#m,G,Gm,G#,G#m,A,Am,Bb,Bbm,B,Bm").split(",");
3009+ */
3010+ // if (m_enharmonic_preference == "#") {
3011+ // prefer #'s
3012+ m_majors = QString("C,G,D,A,E,B,F#,C#,G#,D#,A#,F").split(",");
3013+ m_minors = QString("Am,Em,Bm,F#m,C#m,G#m,D#m,A#m,Fm,Cm,Gm,Dm").split(",");
3014+ m_semitoneList = QString("C,Cm,C#,C#m,D,Dm,D#,D#m,E,Em,F,Fm,F#,F#m,G,Gm,G#,G#m,A,Am,A#,A#m,B,Bm").split(",");
3015+ /* } else {
3016+ // prefer b's
3017+ m_majors = QString("C,G,D,A,E,B,Gb,Db,Ab,Eb,Bb,F").split(",");
3018+ m_minors = QString("Am,Em,Bm,Gbm,Dbm,Abm,Ebm,Bbm,Fm,Cm,Gm,Dm").split(",");
3019+ m_semitoneList = QString("C,Cm,Db,Dbm,D,Dm,Eb,Ebm,E,Em,F,Fm,Gb,Gbm,G,Gm,Ab,Abm,A,Am,Bb,Bbm,B,Bm").split(",");
3020+ }
3021+ */
3022+ /*
3023+ // OK notation
3024+ m_majors = QString("1,2,3,4,5,6,7,8,9,10,11,12").split("d,");
3025+ m_minors = QString("1,2,3,4,5,6,7,8,9,10,11,12").split("m,");
3026+ m_semitoneList = QString("1d,10m,8d,5m,3d,12m,10d,7m,5d,2m,12d,9m,7d,4m,2d,11m,9d,6m,4d,1m,11d,8m,6d,Bm").split(",");
3027+ */
3028+
3029+ /*
3030+ QHash<QString, QStringList> hash;
3031+
3032+ hash["1d"] = "C";
3033+ hash["2d"] = "G";
3034+ hash["3d"] = "D";
3035+ hash["4d"] = "A";
3036+ hash["5d"] = "E";
3037+ hash["6d"] = "B";
3038+ hash["7d"] = "F#,Gb";
3039+ hash["8d"] = "C#,Db";
3040+ hash["9d"] = "G#,Ab";
3041+ hash["10d"] = "D#,Eb";
3042+ hash["11d"] = "A#,Bb";
3043+ hash["12d"] = "F";
3044+
3045+ hash["1m"] = "Am";
3046+ hash["2m"] = "Em";
3047+ hash["3m"] = "Bm";
3048+ hash["4m"] = "F#m,Gbm";
3049+ hash["5m"] = "C#m,Dbm";
3050+ hash["6m"] = "G#m,Abm";
3051+ hash["7m"] = "D#m,Ebm";
3052+ hash["8m"] = "A#m,Bbm";
3053+ hash["9m"] = "Fm";
3054+ hash["10m"] = "Cm";
3055+ hash["11m"] = "Gm";
3056+ hash["12m"] = "Dm";
3057+
3058+ // majors minors
3059+ hash["1"] = { "C", "Am" };
3060+ hash["2"] = { "G", "Em" };
3061+ hash["3"] = { "D", "Bm" };
3062+ hash["4"] = { "A", "F#m,Gbm" };
3063+ hash["5"] = { "E", "C#m,Dbm" };
3064+ hash["6"] = { "B", "G#m,Abm" };
3065+ hash["7"] = { "F#,Gb", "D#m,Ebm" };
3066+ hash["8"] = { "C#,Db", "A#m,Bbm" };
3067+ hash["9"] = { "G#,Ab", "Fm" };
3068+ hash["10"] = { "D#,Eb", "Cm" };
3069+ hash["11"] = { "A#,Bb", "Gm" };
3070+ hash["12"] = { "F", "Dm" };
3071+
3072+ */
3073+
3074+}
3075
3076=== added file 'mixxx/src/library/selector/selectorlibrarytablemodel.h'
3077--- mixxx/src/library/selector/selectorlibrarytablemodel.h 1970-01-01 00:00:00 +0000
3078+++ mixxx/src/library/selector/selectorlibrarytablemodel.h 2012-06-27 11:06:21 +0000
3079@@ -0,0 +1,103 @@
3080+// Created 3/17/2012 by Keith Salisbury (keithsalisbury@gmail.com)
3081+
3082+#ifndef SELECTORLIBRARYTABLEMODEL_H_
3083+#define SELECTORLIBRARYTABLEMODEL_H_
3084+
3085+#include <QModelIndexList>
3086+#include "library/librarytablemodel.h"
3087+#include "controlobjectthreadmain.h"
3088+
3089+
3090+class ControlObjectThreadMain;
3091+
3092+class SelectorLibraryTableModel : public LibraryTableModel
3093+{
3094+ Q_OBJECT
3095+ public:
3096+ SelectorLibraryTableModel(QObject* parent, TrackCollection* pTrackCollection);
3097+ virtual ~SelectorLibraryTableModel();
3098+
3099+ virtual void search(const QString& searchText);
3100+ virtual bool isColumnInternal(int column);
3101+ virtual int rowCount();
3102+ void active(bool value);
3103+
3104+ bool currentTrackGenreExists();
3105+ bool currentTrackBpmExists();
3106+ bool currentTrackYearExists();
3107+ bool currentTrackRatingExists();
3108+ bool currentTrackKeyExists();
3109+
3110+ public slots:
3111+ void filterByGenre(bool value);
3112+ void filterByBpm(bool value, int range);
3113+ void filterByYear(bool value);
3114+ void filterByRating(bool value);
3115+ void filterByKey(bool value);
3116+ void filterByKey4th(bool value);
3117+ void filterByKey5th(bool value);
3118+ void filterByKeyRelative(bool value);
3119+
3120+ /*
3121+ void updateFilter(
3122+ bool filterByGenre,
3123+ bool filterByBpm,
3124+ bool filterByYear,
3125+ bool filterByRating,
3126+ bool filterByKey,
3127+ bool filterByHarmonicKey
3128+ );
3129+ */
3130+ private slots:
3131+ void slotSearch(const QString& searchText);
3132+ void slotPlayingDeckChanged(int deck);
3133+ void slotChannel1BpmChanged(double value);
3134+ void slotFiltersChanged();
3135+ signals:
3136+ void filtersChanged();
3137+ void currentTrackInfoChanged();
3138+ void doSearch(const QString& searchText);
3139+ private:
3140+
3141+ bool m_bActive;
3142+ void updateFilterText();
3143+ QString adjustPitchBy(QString pitch, int change);
3144+ void setRate();
3145+ float m_rate;
3146+ bool m_bFilterGenre;
3147+ bool m_bFilterBpm;
3148+ int m_iFilterBpmRange;
3149+ bool m_bFilterYear;
3150+ bool m_bFilterRating;
3151+ bool m_bFilterKey;
3152+ bool m_bFilterKey4th;
3153+ bool m_bFilterKey5th;
3154+ bool m_bFilterKeyRelative;
3155+
3156+ // Current Track Properties
3157+ QString m_sCurrentTrackGenre;
3158+ float m_fCurrentTrackBpm;
3159+ QString m_sCurrentTrackYear;
3160+ int m_iCurrentTrackRating;
3161+ QString m_sCurrentTrackKey;
3162+
3163+ QStringList m_semitoneList;
3164+ QStringList m_majors;
3165+ QStringList m_minors;
3166+
3167+ QString m_pChannel;
3168+ QString m_filterString;
3169+ TrackPointer m_pLoadedTrack;
3170+ ControlObjectThreadMain* m_channelBpm;
3171+ void initializeHarmonicsData();
3172+ QString getHarmonicKeys(QString key) const;
3173+
3174+};
3175+
3176+
3177+
3178+
3179+#endif
3180+
3181+
3182+
3183
3184=== added file 'mixxx/src/mixxxcontrol.cpp'
3185--- mixxx/src/mixxxcontrol.cpp 1970-01-01 00:00:00 +0000
3186+++ mixxx/src/mixxxcontrol.cpp 2012-06-27 11:06:21 +0000
3187@@ -0,0 +1,183 @@
3188+#include <QtCore>
3189+#include <QtXml>
3190+#include "mixxxcontrol.h"
3191+
3192+/** MixxxControl constructor */
3193+MixxxControl::MixxxControl(QString controlobject_group, QString controlobject_value,
3194+ QString controlobject_description, MidiOption midioption)
3195+{
3196+ m_strCOGroup = controlobject_group;
3197+ m_strCOValue = controlobject_value;
3198+ m_strCODescription = controlobject_description;
3199+ m_midiOption = midioption;
3200+
3201+ m_thresholdMinimum = 0.0f;
3202+ m_thresholdMaximum = 1.0f;
3203+}
3204+
3205+/** Constructor that unserializes a MixxxControl object from a <control> or <output>
3206+ node block in our MIDI mapping XML file.
3207+*/
3208+MixxxControl::MixxxControl(QDomElement& parentNode, bool isOutputNode)
3209+{
3210+ QDomElement groupNode = parentNode.firstChildElement("group");
3211+ QDomElement keyNode = parentNode.firstChildElement("key");
3212+ QDomElement descriptionNode = parentNode.firstChildElement("description");
3213+
3214+ m_strCOGroup = groupNode.text();
3215+ m_strCOValue = keyNode.text();
3216+ m_strCODescription = descriptionNode.text();
3217+
3218+ QDomElement optionsNode = parentNode.firstChildElement("options");
3219+
3220+ // At the moment, use one element, in future iterate through options
3221+ QString strMidiOption;
3222+ if (optionsNode.hasChildNodes()) {
3223+ strMidiOption = optionsNode.firstChild().nodeName().toLower();
3224+ } else {
3225+ strMidiOption = "normal";
3226+ }
3227+
3228+ if (strMidiOption == "normal") // can't switch() on a string
3229+ m_midiOption = MIDI_OPT_NORMAL;
3230+ else if (strMidiOption == "invert")
3231+ m_midiOption = MIDI_OPT_INVERT;
3232+ else if (strMidiOption == "rot64")
3233+ m_midiOption = MIDI_OPT_ROT64;
3234+ else if (strMidiOption == "rot64inv")
3235+ m_midiOption = MIDI_OPT_ROT64_INV;
3236+ else if (strMidiOption == "rot64fast")
3237+ m_midiOption = MIDI_OPT_ROT64_FAST;
3238+ else if (strMidiOption == "diff")
3239+ m_midiOption = MIDI_OPT_DIFF;
3240+ else if (strMidiOption == "button")
3241+ m_midiOption = MIDI_OPT_BUTTON;
3242+ else if (strMidiOption == "switch")
3243+ m_midiOption = MIDI_OPT_SWITCH;
3244+ else if (strMidiOption == "hercjog")
3245+ m_midiOption = MIDI_OPT_HERC_JOG;
3246+ else if (strMidiOption == "spread64")
3247+ m_midiOption = MIDI_OPT_SPREAD64;
3248+ else if (strMidiOption == "selectknob")
3249+ m_midiOption = MIDI_OPT_SELECTKNOB;
3250+ else if (strMidiOption == "soft-takeover")
3251+ m_midiOption = MIDI_OPT_SOFT_TAKEOVER;
3252+ else if (strMidiOption == "script-binding")
3253+ m_midiOption = MIDI_OPT_SCRIPT;
3254+ else {
3255+ m_midiOption = MIDI_OPT_NORMAL;
3256+ qWarning() << "Unknown midioption" << strMidiOption << "in" << __FILE__;
3257+ }
3258+
3259+ //Parse threshold stuff, only used for output.
3260+ if (isOutputNode) {
3261+
3262+ //TODO: Parse threshold stuff
3263+ QDomElement minNode = parentNode.firstChildElement("minimum");
3264+ QDomElement maxNode = parentNode.firstChildElement("maximum");
3265+
3266+ bool ok = false;
3267+ if (!minNode.isNull()) {
3268+ m_thresholdMinimum = minNode.text().toFloat(&ok);
3269+ } else {
3270+ ok = false;
3271+ }
3272+
3273+ if (!ok) //If not a float, or node wasn't defined
3274+ m_thresholdMinimum = 0.0f;
3275+
3276+ if (!maxNode.isNull()) {
3277+ m_thresholdMaximum = maxNode.text().toFloat(&ok);
3278+ } else {
3279+ ok = false;
3280+ }
3281+
3282+ if (!ok) //If not a float, or node wasn't defined
3283+ m_thresholdMaximum = 1.0f;
3284+ }
3285+}
3286+
3287+void MixxxControl::serializeToXML(QDomElement& parentNode, bool isOutputNode) const
3288+{
3289+ QDomText text;
3290+ QDomDocument nodeMaker;
3291+ QDomElement tagNode;
3292+
3293+ //Control object group
3294+ tagNode = nodeMaker.createElement("group");
3295+ text = nodeMaker.createTextNode(this->getControlObjectGroup());
3296+ tagNode.appendChild(text);
3297+ parentNode.appendChild(tagNode);
3298+
3299+ //Control object value
3300+ tagNode = nodeMaker.createElement("key"); //WTF worst name ever
3301+ text = nodeMaker.createTextNode(this->getControlObjectValue());
3302+ tagNode.appendChild(text);
3303+ parentNode.appendChild(tagNode);
3304+
3305+ //Control object description
3306+ tagNode = nodeMaker.createElement("description");
3307+ text = nodeMaker.createTextNode(this->getControlObjectDescription());
3308+ tagNode.appendChild(text);
3309+ parentNode.appendChild(tagNode);
3310+
3311+ //Midi option (slightly different format)
3312+ QDomElement optionsNode = nodeMaker.createElement("options");
3313+ QString strMidiOption;
3314+ int iMidiOption = this->getMidiOption();
3315+ if (iMidiOption == MIDI_OPT_NORMAL) // can't switch() on a string
3316+ strMidiOption = "normal";
3317+ else if (iMidiOption == MIDI_OPT_INVERT)
3318+ strMidiOption = "invert";
3319+ else if (iMidiOption == MIDI_OPT_ROT64)
3320+ strMidiOption = "rot64";
3321+ else if (iMidiOption == MIDI_OPT_ROT64_INV)
3322+ strMidiOption = "rot64inv";
3323+ else if (iMidiOption == MIDI_OPT_ROT64_FAST)
3324+ strMidiOption = "rot64fast";
3325+ else if (iMidiOption == MIDI_OPT_DIFF)
3326+ strMidiOption = "diff";
3327+ else if (iMidiOption == MIDI_OPT_BUTTON)
3328+ strMidiOption = "button";
3329+ else if (iMidiOption == MIDI_OPT_SWITCH)
3330+ strMidiOption = "switch";
3331+ else if (iMidiOption == MIDI_OPT_HERC_JOG)
3332+ strMidiOption = "hercjog";
3333+ else if (iMidiOption == MIDI_OPT_SPREAD64)
3334+ strMidiOption = "spread64";
3335+ else if (iMidiOption == MIDI_OPT_SELECTKNOB)
3336+ strMidiOption = "selectknob";
3337+ else if (iMidiOption == MIDI_OPT_SOFT_TAKEOVER)
3338+ strMidiOption = "soft-takeover";
3339+ else if (iMidiOption == MIDI_OPT_SCRIPT)
3340+ strMidiOption = "script-binding";
3341+ else {
3342+ strMidiOption = "Unknown";
3343+ qWarning() << "Unknown midioption in" << __FILE__;
3344+ }
3345+
3346+ QDomElement singleOption = nodeMaker.createElement(strMidiOption);
3347+ optionsNode.appendChild(singleOption);
3348+ parentNode.appendChild(optionsNode);
3349+
3350+ //If we're writing to an <output> block, write our threshold params
3351+ if (isOutputNode)
3352+ {
3353+ //TODO: Write threshold blocks
3354+ tagNode = nodeMaker.createElement("minimum");
3355+ text = nodeMaker.createTextNode(QString("%1").arg(this->getThresholdMinimum()));
3356+ tagNode.appendChild(text);
3357+ parentNode.appendChild(tagNode);
3358+
3359+ tagNode = nodeMaker.createElement("maximum");
3360+ text = nodeMaker.createTextNode(QString("%1").arg(this->getThresholdMaximum()));
3361+ tagNode.appendChild(text);
3362+ parentNode.appendChild(tagNode);
3363+ }
3364+}
3365+
3366+
3367+uint qHash(const MixxxControl& key)
3368+{
3369+ return (qHash(key.getControlObjectGroup() + key.getControlObjectValue()));
3370+}
3371
3372=== added file 'mixxx/src/mixxxcontrol.h'
3373--- mixxx/src/mixxxcontrol.h 1970-01-01 00:00:00 +0000
3374+++ mixxx/src/mixxxcontrol.h 2012-06-27 11:06:21 +0000
3375@@ -0,0 +1,88 @@
3376+#ifndef _MIXXXCONTROL_H_
3377+#define _MIXXXCONTROL_H_
3378+
3379+#include <QDebug>
3380+#include "configobject.h"
3381+
3382+typedef enum {
3383+ MIDI_OPT_NORMAL = 0,
3384+ MIDI_OPT_INVERT = 1,
3385+ MIDI_OPT_ROT64 = 2,
3386+ MIDI_OPT_ROT64_INV = 3,
3387+ MIDI_OPT_ROT64_FAST = 4,
3388+ MIDI_OPT_DIFF = 5,
3389+ MIDI_OPT_BUTTON = 6, // Button Down (!=00) and Button Up (00) events happen together
3390+ MIDI_OPT_SWITCH = 7, // Button Down (!=00) and Button Up (00) events happen seperately
3391+ MIDI_OPT_HERC_JOG = 8, // Generic hercules wierd range correction
3392+ MIDI_OPT_SPREAD64 = 9, // Accelerated difference from 64
3393+ MIDI_OPT_SELECTKNOB = 10,// Relative knob which can be turned forever and outputs a signed value.
3394+
3395+ MIDI_OPT_SOFT_TAKEOVER = 40,// Prevents sudden changes when hardware position differs from software value
3396+
3397+ MIDI_OPT_SCRIPT = 50,// Maps a MIDI control to a custom MixxxScript function
3398+} MidiOption;
3399+
3400+/** Note: The hash table in the MIDI mapping class maps MidiCommands onto MixxxControls! */
3401+
3402+class MixxxControl
3403+{
3404+ public:
3405+ MixxxControl(QString controlobject_group="", QString controlobject_value="",
3406+ QString controlobject_description="",
3407+ MidiOption midioption=MIDI_OPT_NORMAL);
3408+ MixxxControl(QDomElement& controlNode, bool isOutputNode=false);
3409+ ~MixxxControl() {};
3410+ void setControlObjectGroup(QString controlobject_group) { m_strCOGroup = controlobject_group; };
3411+ void setControlObjectValue(QString controlobject_value) { m_strCOValue = controlobject_value; };
3412+ void setControlObjectDescription(QString controlobject_description) { m_strCODescription = controlobject_description; };
3413+ void setMidiOption(MidiOption midioption) { m_midiOption = midioption; };
3414+ void setThresholdMinimum(float min) { m_thresholdMinimum = min; };
3415+ void setThresholdMaximum(float max) { m_thresholdMaximum = max; };
3416+ QString getControlObjectGroup() const { return m_strCOGroup; };
3417+ QString getControlObjectValue() const { return m_strCOValue; };
3418+ QString getControlObjectDescription() const { return m_strCODescription; };
3419+ MidiOption getMidiOption() const { return m_midiOption; };
3420+ float getThresholdMinimum() const { return m_thresholdMinimum; };
3421+ float getThresholdMaximum() const { return m_thresholdMaximum; };
3422+ void serializeToXML(QDomElement& parentNode, bool isOutputNode=false) const;
3423+ bool operator==(const MixxxControl& other) const {
3424+ return ((m_strCOGroup == other.getControlObjectGroup()) &&
3425+ (m_strCOValue == other.getControlObjectValue()) &&
3426+ (m_strCODescription == other.getControlObjectDescription()) &&
3427+ (m_midiOption == other.getMidiOption()));
3428+ };
3429+ bool isNull() { return (m_strCOGroup == "" && m_strCOValue == ""); };
3430+ private:
3431+ QString m_strCOGroup;
3432+ QString m_strCOValue;
3433+ QString m_strCODescription;
3434+ MidiOption m_midiOption;
3435+
3436+ /** These next parameters are used for MIDI output, when we're monitoring
3437+ the value of some control and sending output when it hits some threshold
3438+ (and stuff like that). */
3439+ float m_thresholdMinimum;
3440+ float m_thresholdMaximum;
3441+};
3442+
3443+inline bool operator<(const MixxxControl &first, const MixxxControl &second)
3444+{
3445+ return ((first.getControlObjectGroup() + first.getControlObjectValue()) <
3446+ (second.getControlObjectGroup() + second.getControlObjectValue()));
3447+}
3448+
3449+/** Hash function needed so we can use MixxxControl in a QHash table */
3450+uint qHash(const MixxxControl& key);
3451+
3452+/*
3453+QDebug operator<<(QDebug dbg, MixxxControl& control)
3454+{
3455+ dbg.space() << control.getControlObjectGroup();
3456+ dbg.space() << control.getControlObjectValue();
3457+ dbg.space() << control.getMidiOption();
3458+
3459+ return dbg.space();
3460+}*/
3461+
3462+#endif
3463+
3464
3465=== added file 'mixxx/src/softtakeover.cpp'
3466--- mixxx/src/softtakeover.cpp 1970-01-01 00:00:00 +0000
3467+++ mixxx/src/softtakeover.cpp 2012-06-27 11:06:21 +0000
3468@@ -0,0 +1,115 @@
3469+/***************************************************************************
3470+ softtakeover.cpp - description
3471+ ----------------
3472+ begin : Sat Mar 26 2011
3473+ copyright : (C) 2011 by Sean M. Pappalardo
3474+ email : spappalardo@mixxx.org
3475+ ***************************************************************************/
3476+
3477+/***************************************************************************
3478+ * *
3479+ * This program is free software; you can redistribute it and/or modify *
3480+ * it under the terms of the GNU General Public License as published by *
3481+ * the Free Software Foundation; either version 2 of the License, or *
3482+ * (at your option) any later version. *
3483+ * *
3484+ ***************************************************************************/
3485+
3486+#include "softtakeover.h"
3487+#include <math.h> // for fabs()
3488+#include <qdatetime.h>
3489+
3490+// HACK: remove this after Control 2.0 is here
3491+#include "controlpotmeter.h"
3492+
3493+// qint64 currentTimeMsecs() {
3494+uint SoftTakeover::currentTimeMsecs() {
3495+ QDateTime currDT = QDateTime::currentDateTime();
3496+
3497+ // toMSecsSinceEpoch() is preferred since it's only one QDateTime call but
3498+ // it requires Qt 4.7. Use it instead when something more substantial
3499+ // elsewhere in Mixxx also requires Qt 4.7.
3500+ //qint64 t = dt.toMSecsSinceEpoch(); // Requires Qt 4.7
3501+ uint t = currDT.toTime_t()*1000+currDT.toString("zzz").toUInt();
3502+ return t;
3503+}
3504+
3505+// For legacy Controls
3506+void SoftTakeover::enable(QString group, QString name) {
3507+ MixxxControl mixxxControl = MixxxControl(group,name);
3508+ enable(mixxxControl);
3509+}
3510+
3511+void SoftTakeover::enable(MixxxControl control) {
3512+ if (!m_times.contains(control)) m_times.insert(control,currentTimeMsecs());
3513+}
3514+
3515+// For legacy Controls
3516+void SoftTakeover::disable(QString group, QString name) {
3517+ MixxxControl mixxxControl = MixxxControl(group,name);
3518+ disable(mixxxControl);
3519+}
3520+
3521+void SoftTakeover::disable(MixxxControl control) {
3522+ m_times.remove(control);
3523+}
3524+
3525+// For legacy Controls
3526+bool SoftTakeover::ignore(QString group, QString name, float newValue) {
3527+ MixxxControl mixxxControl = MixxxControl(group,name);
3528+ return ignore(mixxxControl,newValue);
3529+}
3530+
3531+bool SoftTakeover::ignore(MixxxControl mc, float newValue, bool midiVal) {
3532+ bool ignore = false;
3533+ QString message;
3534+ if (m_times.contains(mc)) {
3535+ // We only want to ignore the MIDI controller when all of the following are true:
3536+ // - its new value is far away from the MixxxControl
3537+ // - it's been awhile since the last MIDI message for this control affected it
3538+
3539+ // 3/128 units away from the current is enough to catch fast non-sequential moves
3540+ // but not cause an audially noticeable jump.
3541+ float threshold = 3;
3542+
3543+ ControlObject* temp = ControlObject::getControl(
3544+ ConfigKey(mc.getControlObjectGroup(), mc.getControlObjectValue()));
3545+
3546+ if (temp == NULL) return ignore;
3547+
3548+ if (!midiVal) {
3549+ // These defaults will effectively disable soft-takeover for this pass
3550+ // (causing the control to jump to the new value regardless)
3551+ // if there's a problem with the below CO being NULL
3552+ double maxValue=10000000; // Anything, just higher than any CO can go
3553+ double minValue=0;
3554+
3555+ // HACK until we have Control 2.0. It can't come soon enough...
3556+ ControlPotmeter* cpo = dynamic_cast<ControlPotmeter*>(temp); // for getMax/getMin
3557+ if (cpo != NULL) {
3558+ maxValue = cpo->getMax();
3559+ minValue = cpo->getMin();
3560+ }
3561+ // End hack
3562+
3563+ double scaleFactor = maxValue-minValue;
3564+ threshold = scaleFactor*(threshold/128);
3565+ }
3566+
3567+ double oldValue;
3568+ if (midiVal) oldValue = temp->GetMidiValue();
3569+ else oldValue = temp->get();
3570+ double difference = oldValue - newValue;
3571+
3572+ uint currentTime = currentTimeMsecs();
3573+ if (fabs(difference)>threshold
3574+ && (currentTime - m_times.value(mc)) > SUBSEQUENT_VALUE_OVERRIDE_TIME) {
3575+ ignore = true;
3576+ }
3577+ if (!ignore) {
3578+ // Update the time only if the value is not ignored
3579+ m_times.insert(mc,currentTime); // Replaces any previous value for this MixxxControl
3580+ }
3581+ }
3582+ return ignore;
3583+}
3584
3585=== added file 'mixxx/src/softtakeover.h'
3586--- mixxx/src/softtakeover.h 1970-01-01 00:00:00 +0000
3587+++ mixxx/src/softtakeover.h 2012-06-27 11:06:21 +0000
3588@@ -0,0 +1,50 @@
3589+/***************************************************************************
3590+ softtakeover.h - description
3591+ --------------
3592+ begin : Thu Mar 17 2011
3593+ copyright : (C) 2011 by Sean M. Pappalardo
3594+ email : spappalardo@mixxx.org
3595+ ***************************************************************************/
3596+
3597+/***************************************************************************
3598+ * *
3599+ * This program is free software; you can redistribute it and/or modify *
3600+ * it under the terms of the GNU General Public License as published by *
3601+ * the Free Software Foundation; either version 2 of the License, or *
3602+ * (at your option) any later version. *
3603+ * *
3604+ ***************************************************************************/
3605+#ifndef SOFTTAKEOVER_H
3606+#define SOFTTAKEOVER_H
3607+
3608+#include "mixxxcontrol.h"
3609+
3610+class SoftTakeover {
3611+
3612+ public:
3613+ /** Enable soft-takeover for the given Control
3614+ It's okay to call this on a Control that's already enabled. */
3615+ void enable(MixxxControl control);
3616+ void enable(QString group, QString name);
3617+ /** Disable soft-takeover for the given Control */
3618+ void disable(MixxxControl control);
3619+ void disable(QString group, QString name);
3620+ /** Check to see if the new value for the Control should be ignored */
3621+ bool ignore(MixxxControl control, float newValue, bool midiVal = false);
3622+ /** For legacy Controls */
3623+ bool ignore(QString group, QString name, float newValue);
3624+
3625+ private:
3626+ /** If a new value is received within this amount of time,
3627+ jump to it regardless. This allows quickly whipping controls to work
3628+ while retaining the benefits of soft-takeover for slower movements.
3629+
3630+ Setting this too high will defeat the purpose of soft-takeover.*/
3631+ static const uint SUBSEQUENT_VALUE_OVERRIDE_TIME = 50; // Milliseconds
3632+ //qint64 currentTimeMsecs();
3633+ //QHash<MixxxControl,qint64> m_times;
3634+ uint currentTimeMsecs();
3635+ QHash<MixxxControl,uint> m_times;
3636+};
3637+
3638+#endif
3639\ No newline at end of file
3640
3641=== added file 'mixxx/src/test/analyserwavesummarytest.cpp'
3642--- mixxx/src/test/analyserwavesummarytest.cpp 1970-01-01 00:00:00 +0000
3643+++ mixxx/src/test/analyserwavesummarytest.cpp 2012-06-27 11:06:21 +0000
3644@@ -0,0 +1,102 @@
3645+#include <gtest/gtest.h>
3646+#include <QDebug>
3647+
3648+#include "trackinfoobject.h"
3649+#include "analyserwavesummary.h"
3650+
3651+#define BIGBUF_SIZE (1024 * 1024) //Megabyte
3652+#define CANARY_SIZE (1024*4)
3653+#define MAGIC_FLOAT 1234.567890f
3654+#define CANARY_FLOAT 0.0f
3655+
3656+namespace {
3657+
3658+class AnalyserWavesummaryTest: public testing::Test {
3659+ protected:
3660+ virtual void SetUp() {
3661+ qDebug() << "SetUp";
3662+ aw = new AnalyserWavesummary();
3663+ tio = TrackPointer(new TrackInfoObject("foo"));
3664+ //Subpixels per second, from waveformrenderer.cpp:247
3665+ tio->setVisualResampleRate(200);
3666+
3667+ bigbuf = new CSAMPLE[BIGBUF_SIZE];
3668+ for (int i = 0; i < BIGBUF_SIZE; i++)
3669+ bigbuf[i] = MAGIC_FLOAT;
3670+
3671+ //Memory layout for canaryBigBuf looks like
3672+ // [ canary | big buf | canary ]
3673+ //
3674+
3675+ canaryBigBuf = new CSAMPLE[BIGBUF_SIZE + 2*CANARY_SIZE];
3676+ for (int i = 0; i < CANARY_SIZE; i++)
3677+ canaryBigBuf[i] = CANARY_FLOAT;
3678+ for (int i = CANARY_SIZE; i < CANARY_SIZE+BIGBUF_SIZE; i++)
3679+ canaryBigBuf[i] = MAGIC_FLOAT;
3680+ for (int i = CANARY_SIZE+BIGBUF_SIZE; i < 2*CANARY_SIZE+BIGBUF_SIZE; i++)
3681+ canaryBigBuf[i] = CANARY_FLOAT;
3682+ }
3683+
3684+ virtual void TearDown() {
3685+ qDebug() << "TearDown";
3686+ qDebug() << "delete aw";
3687+ delete aw;
3688+ delete [] bigbuf;
3689+ delete [] canaryBigBuf;
3690+ }
3691+
3692+ AnalyserWavesummary* aw;
3693+ TrackPointer tio;
3694+ CSAMPLE* bigbuf;
3695+ CSAMPLE* canaryBigBuf;
3696+};
3697+
3698+//Test to make sure we don't modify the source buffer.
3699+TEST_F(AnalyserWavesummaryTest, simpleAnalyze) {
3700+ aw->initialise(tio, 44100, BIGBUF_SIZE);
3701+ aw->process(bigbuf, BIGBUF_SIZE);
3702+ aw->finalise(tio);
3703+ for (int i = 0; i < BIGBUF_SIZE; i++) {
3704+ EXPECT_FLOAT_EQ(bigbuf[i], MAGIC_FLOAT);
3705+ }
3706+}
3707+
3708+//Basic test to make sure we don't step out of bounds.
3709+TEST_F(AnalyserWavesummaryTest, canary) {
3710+ aw->initialise(tio, 44100, BIGBUF_SIZE);
3711+ aw->process(&canaryBigBuf[CANARY_SIZE], BIGBUF_SIZE);
3712+ aw->finalise(tio);
3713+ for (int i = 0; i < CANARY_SIZE; i++) {
3714+ EXPECT_FLOAT_EQ(canaryBigBuf[i], CANARY_FLOAT);
3715+ }
3716+ for (int i = CANARY_SIZE+BIGBUF_SIZE; i < 2*CANARY_SIZE+BIGBUF_SIZE; i++) {
3717+ EXPECT_FLOAT_EQ(canaryBigBuf[i], CANARY_FLOAT);
3718+ }
3719+}
3720+
3721+//Test to make sure that if an incorrect totalSamples is passed to
3722+//initialise(..) and process(..) is told to process more samples than that,
3723+//that we don't step out of bounds.
3724+TEST_F(AnalyserWavesummaryTest, wrongTotalSamples) {
3725+ aw->initialise(tio, 44100, BIGBUF_SIZE);
3726+ //Process in a loop
3727+ int wrongTotalSamples = BIGBUF_SIZE+1; //Too big by 1 sample...
3728+ //Note that the correct totalSamples would just be BIGBUF_SIZE. :)
3729+ int blockSize = 2*32768;
3730+ for (int i = CANARY_SIZE; i < CANARY_SIZE+wrongTotalSamples; i += blockSize) {
3731+ aw->process(&canaryBigBuf[i], blockSize);
3732+ }
3733+ aw->finalise(tio);
3734+ //Ensure the source buffer is intact
3735+ for (int i = CANARY_SIZE; i < BIGBUF_SIZE; i++) {
3736+ EXPECT_FLOAT_EQ(canaryBigBuf[i], MAGIC_FLOAT);
3737+ }
3738+ //Make sure our canaries are still OK
3739+ for (int i = 0; i < CANARY_SIZE; i++) {
3740+ EXPECT_FLOAT_EQ(canaryBigBuf[i], CANARY_FLOAT);
3741+ }
3742+ for (int i = CANARY_SIZE+BIGBUF_SIZE; i < 2*CANARY_SIZE+BIGBUF_SIZE; i++) {
3743+ EXPECT_FLOAT_EQ(canaryBigBuf[i], CANARY_FLOAT);
3744+ }
3745+}
3746+}
3747
3748=== added file 'mixxx/src/test/midiscriptenginetest.cpp'
3749--- mixxx/src/test/midiscriptenginetest.cpp 1970-01-01 00:00:00 +0000
3750+++ mixxx/src/test/midiscriptenginetest.cpp 2012-06-27 11:06:21 +0000
3751@@ -0,0 +1,95 @@
3752+
3753+#include <gtest/gtest.h>
3754+#include <QDebug>
3755+#include <QApplication>
3756+#include <QObject>
3757+#include <QFile>
3758+
3759+#include "controlobject.h"
3760+#include "configobject.h"
3761+#include "midi/midiscriptengine.h"
3762+
3763+namespace {
3764+
3765+class MidiScriptEngineTest : public testing::Test {
3766+ protected:
3767+ virtual void SetUp() {
3768+ qDebug() << "SetUp";
3769+ static int argc = 1;
3770+ static char** argv = NULL;
3771+ app = new QApplication(argc, argv);
3772+ MidiDevice* pDevice = NULL;
3773+ scriptEngine = new MidiScriptEngine(pDevice);
3774+ scriptEngine->setMidiDebug(false);
3775+ scriptEngine->setMidiPopups(false);
3776+ scriptEngine->moveToThread(scriptEngine);
3777+ scriptEngine->start();
3778+ while(!scriptEngine->isReady()) { }
3779+ }
3780+
3781+ virtual void TearDown() {
3782+ qDebug() << "TearDown";
3783+ scriptEngine->gracefulShutdown(QList<QString>());
3784+ scriptEngine->wait();
3785+ delete scriptEngine;
3786+ delete app;
3787+ }
3788+
3789+ QApplication *app;
3790+ MidiScriptEngine *scriptEngine;
3791+};
3792+
3793+TEST_F(MidiScriptEngineTest, commonScriptHasNoErrors) {
3794+ // ConfigObject<ConfigValue> config("~/.mixxx/mixxx.cfg");
3795+ // QString commonScript = config.getConfigPath() + "/" +
3796+ // "/midi/midi-mappings-scripts.js";
3797+ QString commonScript = "./res/midi/midi-mappings-scripts.js";
3798+ scriptEngine->evaluate(commonScript);
3799+ EXPECT_FALSE(scriptEngine->hasErrors(commonScript));
3800+}
3801+
3802+TEST_F(MidiScriptEngineTest, scriptSetValue) {
3803+ QString script = "test.js";
3804+ QFile f(script);
3805+ f.open(QIODevice::ReadWrite | QIODevice::Truncate);
3806+ f.write("setValue = function() { engine.setValue('[Channel1]', 'co', 1.0); }\n");
3807+ f.close();
3808+
3809+ scriptEngine->evaluate(script);;
3810+ EXPECT_FALSE(scriptEngine->hasErrors(script));
3811+
3812+ ControlObject *co = new ControlObject(ConfigKey("[Channel1]", "co"));
3813+ co->set(0.0);
3814+ scriptEngine->execute("setValue");
3815+ ControlObject::sync();
3816+ EXPECT_DOUBLE_EQ(co->get(), 1.0f);
3817+
3818+ delete co;
3819+ co = NULL;
3820+
3821+ f.remove();
3822+}
3823+
3824+TEST_F(MidiScriptEngineTest, scriptGetSetValue) {
3825+ QString script = "test.js";
3826+ QFile f(script);
3827+ f.open(QIODevice::ReadWrite | QIODevice::Truncate);
3828+ f.write("getSetValue = function() { var val = engine.getValue('[Channel1]', 'co'); engine.setValue('[Channel1]', 'co', val + 1); }\n");
3829+ f.close();
3830+
3831+ scriptEngine->evaluate(script);;
3832+ EXPECT_FALSE(scriptEngine->hasErrors(script));
3833+
3834+ ControlObject *co = new ControlObject(ConfigKey("[Channel1]", "co"));
3835+ co->set(0.0);
3836+ scriptEngine->execute("getSetValue");
3837+ ControlObject::sync();
3838+ EXPECT_DOUBLE_EQ(co->get(), 1.0f);
3839+
3840+ delete co;
3841+ co = NULL;
3842+
3843+ f.remove();
3844+}
3845+
3846+}
3847
3848=== added file 'mixxx/src/track/beatmatrix.cpp'
3849--- mixxx/src/track/beatmatrix.cpp 1970-01-01 00:00:00 +0000
3850+++ mixxx/src/track/beatmatrix.cpp 2012-06-27 11:06:21 +0000
3851@@ -0,0 +1,275 @@
3852+#include <QtDebug>
3853+#include <QMutexLocker>
3854+
3855+#include "track/beatmatrix.h"
3856+
3857+BeatMatrix::BeatMatrix(TrackPointer pTrack, const QByteArray* pByteArray)
3858+ : QObject(),
3859+ m_mutex(QMutex::Recursive),
3860+ m_iSampleRate(pTrack->getSampleRate()) {
3861+ if (pByteArray != NULL) {
3862+ readByteArray(pByteArray);
3863+ }
3864+}
3865+
3866+BeatMatrix::~BeatMatrix() {
3867+}
3868+
3869+unsigned int BeatMatrix::numBeats() const {
3870+ return m_beatList.size();
3871+}
3872+
3873+QByteArray* BeatMatrix::toByteArray() const {
3874+ QMutexLocker locker(&m_mutex);
3875+ // No guarantees BeatLists are made of a data type which located adjacent
3876+ // items in adjacent memory locations.
3877+ double* pBuffer = new double[m_beatList.size()];
3878+ for (int i = 0; i < m_beatList.size(); ++i) {
3879+ pBuffer[i] = m_beatList[i];
3880+ }
3881+ QByteArray* pByteArray = new QByteArray((char*)pBuffer, sizeof(pBuffer[0]) * m_beatList.size());
3882+ delete [] pBuffer;
3883+ return pByteArray;
3884+}
3885+
3886+void BeatMatrix::readByteArray(const QByteArray* pByteArray) {
3887+ if (pByteArray->size() % sizeof(double) != 0) {
3888+ qDebug() << "ERROR: Could not parse BeatMatrix from QByteArray of size" << pByteArray->size();
3889+ return;
3890+ }
3891+
3892+ int numBeats = pByteArray->size() / sizeof(double);
3893+ double* pBuffer = (double*)pByteArray->data();
3894+ for (int i = 0; i < numBeats; ++i) {
3895+ m_beatList.append(pBuffer[i]);
3896+ }
3897+}
3898+
3899+
3900+QString BeatMatrix::getVersion() const {
3901+ QMutexLocker locker(&m_mutex);
3902+ return "BeatMatrix-1.0";
3903+}
3904+
3905+// internal use only
3906+bool BeatMatrix::isValid() const {
3907+ return m_iSampleRate > 0 && m_beatList.size() > 0;
3908+}
3909+
3910+double BeatMatrix::findNextBeat(double dSamples) const {
3911+ return findNthBeat(dSamples, 1);
3912+}
3913+
3914+double BeatMatrix::findPrevBeat(double dSamples) const {
3915+ return findNthBeat(dSamples, -1);
3916+}
3917+
3918+double BeatMatrix::findClosestBeat(double dSamples) const {
3919+ QMutexLocker locker(&m_mutex);
3920+ if (!isValid()) {
3921+ return -1;
3922+ }
3923+ double nextBeat = findNextBeat(dSamples);
3924+ double prevBeat = findPrevBeat(dSamples);
3925+ return (nextBeat - dSamples > dSamples - prevBeat) ? prevBeat : nextBeat;
3926+}
3927+
3928+double BeatMatrix::findNthBeat(double dSamples, int n) const {
3929+ QMutexLocker locker(&m_mutex);
3930+ // Reduce the Sample Offset to a frame offset.
3931+ dSamples = floorf(dSamples/2);
3932+ BeatList::const_iterator it;
3933+ int i;
3934+
3935+ if (!isValid() || n == 0) {
3936+ return -1;
3937+ }
3938+
3939+ if (n > 0) {
3940+ it = qLowerBound(m_beatList.begin(), m_beatList.end(), dSamples);
3941+
3942+ // Count down until n=1
3943+ while (it != m_beatList.end()) {
3944+ if (n == 1) {
3945+ // Return a Sample Offset
3946+ return (*it * 2);
3947+ }
3948+ it++; n--;
3949+ }
3950+ }
3951+ else if (n < 0) {
3952+ it = qUpperBound(m_beatList.begin(), m_beatList.end(), dSamples);
3953+
3954+ // Count up until n=-1
3955+ while (it != m_beatList.begin()) {
3956+ // qUpperBound starts us off at the position just-one-past the last
3957+ // occurence of dSamples-or-smaller in the list. In order to get the
3958+ // last instance of dSamples-or-smaller, we decrement it by 1 before
3959+ // touching it. The guard of this while loop guarantees this does
3960+ // not put us before the start of the loop.
3961+ it--;
3962+ if (n == -1) {
3963+ // Return a Sample Offset
3964+ return (*it * 2);
3965+ }
3966+ n++;
3967+ }
3968+ }
3969+
3970+ return -1;
3971+}
3972+
3973+void BeatMatrix::findBeats(double startSample, double stopSample, QList<double>* pBeatsList) const {
3974+ QMutexLocker locker(&m_mutex);
3975+ if (!isValid() || startSample > stopSample) {
3976+ return;
3977+ }
3978+ BeatList::const_iterator curBeat = qLowerBound(m_beatList.begin(),
3979+ m_beatList.end(),
3980+ startSample);
3981+ BeatList::const_iterator stopBeat = qUpperBound(m_beatList.begin(),
3982+ m_beatList.end(),
3983+ stopSample);
3984+
3985+ for (; curBeat != stopBeat; curBeat++) {
3986+ pBeatsList->append(*curBeat);
3987+ }
3988+}
3989+
3990+bool BeatMatrix::hasBeatInRange(double startSample, double stopSample) const {
3991+ QMutexLocker locker(&m_mutex);
3992+ if (!isValid() || startSample > stopSample) {
3993+ return false;
3994+ }
3995+ BeatList::const_iterator startBeat = qLowerBound(m_beatList.begin(),
3996+ m_beatList.end(),
3997+ startSample);
3998+ BeatList::const_iterator stopBeat = qUpperBound(m_beatList.begin(),
3999+ m_beatList.end(),
4000+ stopSample);
4001+
4002+ if (startBeat != stopBeat)
4003+ return true;
4004+ return false;
4005+}
4006+
4007+double BeatMatrix::getBpm() const {
4008+ QMutexLocker locker(&m_mutex);
4009+ if (!isValid()) {
4010+ return -1;
4011+ }
4012+
4013+ // TODO(XXX) not actually correct. We need the true song length.
4014+ double startSample = *m_beatList.begin();
4015+ double stopSample = *(m_beatList.end()-1);
4016+ double songDurationMinutes =
4017+ (stopSample - startSample) / (60.0f * m_iSampleRate);
4018+ return m_beatList.size() / songDurationMinutes;
4019+}
4020+
4021+double BeatMatrix::getBpmRange(double startSample, double stopSample) const {
4022+ QMutexLocker locker(&m_mutex);
4023+ if (!isValid() || startSample > stopSample) {
4024+ return -1;
4025+ }
4026+
4027+ BeatList::const_iterator startBeat = qLowerBound(m_beatList.begin(),
4028+ m_beatList.end(),
4029+ startSample);
4030+ BeatList::const_iterator stopBeat = qUpperBound(m_beatList.begin(),
4031+ m_beatList.end(),
4032+ stopSample);
4033+ double rangeDurationMinutes =
4034+ (stopSample - startSample) / (60.0f * m_iSampleRate);
4035+ // Subtracting returns the number of beats between the samples referred to
4036+ // by the start and end.
4037+ double beatsInRange = stopBeat - startBeat;
4038+
4039+ return beatsInRange / rangeDurationMinutes;
4040+}
4041+
4042+void BeatMatrix::addBeat(double dBeatSample) {
4043+ QMutexLocker locker(&m_mutex);
4044+
4045+ BeatList::iterator it = qLowerBound(m_beatList.begin(),
4046+ m_beatList.end(),
4047+ dBeatSample);
4048+ // Don't insert a duplicate beat. TODO(XXX) determine what epsilon to
4049+ // consider a beat identical to another.
4050+ if (*it == dBeatSample)
4051+ return;
4052+
4053+ m_beatList.insert(it, dBeatSample);
4054+ locker.unlock();
4055+ emit(updated());
4056+}
4057+
4058+void BeatMatrix::removeBeat(double dBeatSample) {
4059+ QMutexLocker locker(&m_mutex);
4060+ BeatList::iterator it = qLowerBound(m_beatList.begin(),
4061+ m_beatList.end(), dBeatSample);
4062+
4063+ // In case there are duplicates, remove every instance of dBeatSample
4064+ // TODO(XXX) add invariant checks against this
4065+ // TODO(XXX) determine what epsilon to consider a beat identical to another
4066+ while (*it == dBeatSample) {
4067+ it = m_beatList.erase(it);
4068+ }
4069+ locker.unlock();
4070+ emit(updated());
4071+}
4072+
4073+void BeatMatrix::moveBeat(double dBeatSample, double dNewBeatSample) {
4074+ QMutexLocker locker(&m_mutex);
4075+
4076+ BeatList::iterator it = qLowerBound(m_beatList.begin(),
4077+ m_beatList.end(), dBeatSample);
4078+
4079+ // Remove all beats from dBeatSample
4080+ while (*it == dBeatSample) {
4081+ it = m_beatList.erase(it);
4082+ }
4083+
4084+ // Now add a beat to dNewBeatSample
4085+ it = qLowerBound(m_beatList.begin(),
4086+ m_beatList.end(), dNewBeatSample);
4087+
4088+ // TODO(XXX) beat epsilon
4089+ if (*it != dNewBeatSample) {
4090+ m_beatList.insert(it, dNewBeatSample);
4091+ }
4092+ locker.unlock();
4093+ emit(updated());
4094+}
4095+
4096+void BeatMatrix::translate(double dNumSamples) {
4097+ QMutexLocker locker(&m_mutex);
4098+ if (!isValid()) {
4099+ return;
4100+ }
4101+
4102+ for (BeatList::iterator it = m_beatList.begin();
4103+ it != m_beatList.end(); ++it) {
4104+ *it += dNumSamples;
4105+ }
4106+ locker.unlock();
4107+ emit(updated());
4108+}
4109+
4110+void BeatMatrix::scale(double dScalePercentage) {
4111+ QMutexLocker locker(&m_mutex);
4112+ if (!isValid()) {
4113+ return;
4114+ }
4115+ for (BeatList::iterator it = m_beatList.begin();
4116+ it != m_beatList.end(); ++it) {
4117+ *it *= dScalePercentage;
4118+ }
4119+ locker.unlock();
4120+ emit(updated());
4121+}
4122+
4123+void BeatMatrix::slotTrackBpmUpdated(double dBpm) {
4124+ //QMutexLocker locker(&m_mutex);
4125+ // TODO(XXX) How do we handle this?
4126+}
4127
4128=== added file 'mixxx/src/track/beatmatrix.h'
4129--- mixxx/src/track/beatmatrix.h 1970-01-01 00:00:00 +0000
4130+++ mixxx/src/track/beatmatrix.h 2012-06-27 11:06:21 +0000
4131@@ -0,0 +1,67 @@
4132+#ifndef BEATMATRIX_H
4133+#define BEATMATRIX_H
4134+
4135+#include <QObject>
4136+#include <QMutex>
4137+
4138+#include "trackinfoobject.h"
4139+#include "track/beats.h"
4140+
4141+// BeatMatrix is an implementation of the Beats interface that implements a list
4142+// of finite beats that have been extracted or otherwise annotated for a track.
4143+class BeatMatrix : public QObject, public Beats {
4144+ Q_OBJECT
4145+ public:
4146+ BeatMatrix(TrackPointer pTrack, const QByteArray* pByteArray=NULL);
4147+ virtual ~BeatMatrix();
4148+
4149+ // See method comments in beats.h
4150+
4151+ virtual Beats::CapabilitiesFlags getCapabilities() const {
4152+ return BEATSCAP_TRANSLATE | BEATSCAP_SCALE | BEATSCAP_ADDREMOVE | BEATSCAP_MOVEBEAT;
4153+ }
4154+
4155+ virtual QByteArray* toByteArray() const;
4156+ virtual QString getVersion() const;
4157+
4158+ ////////////////////////////////////////////////////////////////////////////
4159+ // Beat calculations
4160+ ////////////////////////////////////////////////////////////////////////////
4161+
4162+ virtual double findNextBeat(double dSamples) const;
4163+ virtual double findPrevBeat(double dSamples) const;
4164+ virtual double findClosestBeat(double dSamples) const;
4165+ virtual double findNthBeat(double dSamples, int n) const;
4166+ virtual void findBeats(double startSample, double stopSample, QList<double>* pBeatsList) const;
4167+ virtual bool hasBeatInRange(double startSample, double stopSample) const;
4168+ virtual double getBpm() const;
4169+ virtual double getBpmRange(double startSample, double stopSample) const;
4170+
4171+ ////////////////////////////////////////////////////////////////////////////
4172+ // Beat mutations
4173+ ////////////////////////////////////////////////////////////////////////////
4174+
4175+ virtual void addBeat(double dBeatSample);
4176+ virtual void removeBeat(double dBeatSample);
4177+ virtual void moveBeat(double dBeatSample, double dNewBeatSample);
4178+ virtual void translate(double dNumSamples);
4179+ virtual void scale(double dScalePercentage);
4180+
4181+ signals:
4182+ void updated();
4183+
4184+ private slots:
4185+ void slotTrackBpmUpdated(double bpm);
4186+
4187+ private:
4188+ void readByteArray(const QByteArray* pByteArray);
4189+ // For internal use only.
4190+ bool isValid() const;
4191+ unsigned int numBeats() const;
4192+
4193+ mutable QMutex m_mutex;
4194+ int m_iSampleRate;
4195+ BeatList m_beatList;
4196+};
4197+
4198+#endif /* BEATMATRIX_H */
4199
4200=== added file 'mixxx/src/waveform/glwaveformrenderer.cpp'
4201--- mixxx/src/waveform/glwaveformrenderer.cpp 1970-01-01 00:00:00 +0000
4202+++ mixxx/src/waveform/glwaveformrenderer.cpp 2012-06-27 11:06:21 +0000
4203@@ -0,0 +1,334 @@
4204+/**
4205+
4206+ A license and other info goes here!
4207+
4208+ */
4209+
4210+#include <QDebug>
4211+#include <QDomNode>
4212+#include <QImage>
4213+#include <QObject>
4214+#include <qgl.h>
4215+
4216+#include "glwaveformrenderer.h"
4217+#include "waveformrenderbeat.h"
4218+#include "trackinfoobject.h"
4219+#include "soundsourceproxy.h"
4220+#include "controlobjectthreadmain.h"
4221+#include "controlobject.h"
4222+#include "widget/wwidget.h"
4223+#include "widget/wskincolor.h"
4224+
4225+#define DEFAULT_SECONDS_TO_DISPLAY 4
4226+#define SCALE_TEST 4
4227+
4228+GLWaveformRenderer::GLWaveformRenderer(const char* group) :
4229+m_iWidth(0),
4230+m_iHeight(0),
4231+m_iMax(0),
4232+m_iMin(0),
4233+m_iNumSamples(0),
4234+bgColor(0,0,0),
4235+signalColor(255,255,255),
4236+colorMarker(255,255,255),
4237+colorBeat(255,255,255),
4238+colorCue(255,255,255),
4239+m_iDesiredSecondsToDisplay(DEFAULT_SECONDS_TO_DISPLAY),
4240+m_pSampleBuffer(NULL),
4241+m_pInternalBuffer(NULL),
4242+m_iInternalBufferSize(0)
4243+{
4244+ m_pPlayPos = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey(group,"playposition")));
4245+ connect(m_pPlayPos, SIGNAL(valueChanged(double)), this, SLOT(slotUpdatePlayPos(double)));
4246+
4247+ m_pCOVisualResample = new ControlObject(ConfigKey(group,"VisualResample"));
4248+ m_pCOVerticalScale = new ControlObject(ConfigKey(group, "VisualVerticalScale"));
4249+}
4250+
4251+
4252+GLWaveformRenderer::~GLWaveformRenderer() {
4253+ if(m_pCOVisualResample)
4254+ delete m_pCOVisualResample;
4255+ m_pCOVisualResample = NULL;
4256+
4257+ if(m_pCOVerticalScale)
4258+ delete m_pCOVerticalScale;
4259+ m_pCOVerticalScale = NULL;
4260+
4261+ if(m_pPlayPos)
4262+ delete m_pPlayPos;
4263+ m_pPlayPos = NULL;
4264+
4265+}
4266+
4267+void GLWaveformRenderer::slotUpdatePlayPos(double v) {
4268+ m_dPlayPos = v;
4269+}
4270+
4271+
4272+void GLWaveformRenderer::resize(int w, int h) {
4273+ m_iWidth = w;
4274+ m_iHeight = h;
4275+
4276+ m_pInternalBuffer = new GLfloat[w*3*5];
4277+ m_iInternalBufferSize = w*5;
4278+
4279+ for(int i=0; i<w*5; i++) {
4280+ m_pInternalBuffer[i*3+0] = i-10;
4281+ m_pInternalBuffer[i*3+1] = 0.5;
4282+ m_pInternalBuffer[i*3+2] = 1.0;
4283+
4284+
4285+ }
4286+
4287+ glMatrixMode(GL_PROJECTION);
4288+ glLoadIdentity();
4289+ gluPerspective(60.0, 1.0, 1.0, 1000.0);
4290+ glViewport(0,0,w,h);
4291+
4292+ gluLookAt(0,0,15.0, // look along z-axis
4293+ 0,0,0, // from the origin
4294+ 0,1.0,0); // with y being 'up'
4295+
4296+ setupControlObjects();
4297+}
4298+
4299+void GLWaveformRenderer::setupControlObjects() {
4300+
4301+ // the max positive value of a sample is 32767, (2**15-1)
4302+ // we want the waveforms normalized so that 0-32767 maps to 0-m_iHeight/2
4303+ // (m_iHeight/2) = (32767) / x => x = 32767/(m_iHeight/2)
4304+ int verticalScale = ((1<<15)-1)*2/ m_iHeight;
4305+ m_pCOVerticalScale->set(verticalScale);
4306+
4307+
4308+ // the resample rate is the number of seconds that correspond to one pixel
4309+ // on the visual waveform display.
4310+
4311+ // the reason for using seconds is that we do not know the
4312+ // sample rate of the song that will be loaded.
4313+
4314+ // we calculate this as follows:
4315+
4316+ // secondsPerPixel = desiredSecondsToDisplay / m_iWidth
4317+
4318+ // for now just send the width.. meh
4319+ double secondsPerPixel = double(m_iDesiredSecondsToDisplay)/m_iWidth;
4320+ //m_pCOVisualResample->set(secondsPerPixel);
4321+
4322+ m_pCOVisualResample->set(m_iWidth);
4323+
4324+ qDebug() << "GLWaveformRenderer::setupControlObjects - VisualResample: " << secondsPerPixel << " VerticalScale: " << verticalScale;
4325+
4326+}
4327+
4328+void GLWaveformRenderer::setup(QDomNode node) {
4329+
4330+ bgColor.setNamedColor(WWidget::selectNodeQString(node, "BgColor"));
4331+ bgColor = WSkinColor::getCorrectColor(bgColor);
4332+
4333+ qDebug() << "Got bgColor " << bgColor;
4334+
4335+ signalColor.setNamedColor(WWidget::selectNodeQString(node, "SignalColor"));
4336+ signalColor = WSkinColor::getCorrectColor(signalColor);
4337+
4338+ qDebug() << "Got signalColor " << signalColor;
4339+
4340+ colorMarker.setNamedColor(WWidget::selectNodeQString(node, "MarkerColor"));
4341+ colorMarker = WSkinColor::getCorrectColor(colorMarker);
4342+
4343+ colorBeat.setNamedColor(WWidget::selectNodeQString(node, "BeatColor"));
4344+ colorBeat = WSkinColor::getCorrectColor(colorBeat);
4345+
4346+ colorCue.setNamedColor(WWidget::selectNodeQString(node, "CueColor"));
4347+ colorCue = WSkinColor::getCorrectColor(colorCue);
4348+
4349+}
4350+
4351+
4352+void GLWaveformRenderer::precomputePixmap() {
4353+
4354+}
4355+
4356+bool GLWaveformRenderer::fetchWaveformFromTrack() {
4357+
4358+ if(!m_pTrack)
4359+ return false;
4360+
4361+ QVector<float> *buffer = m_pTrack->getVisualWaveform();
4362+
4363+ if(buffer == NULL)
4364+ return false;
4365+
4366+ m_pSampleBuffer = buffer;
4367+ m_iNumSamples = buffer->size();
4368+
4369+ return true;
4370+}
4371+
4372+
4373+
4374+void GLWaveformRenderer::drawSignalLines() {
4375+
4376+ if(m_pSampleBuffer == NULL) {
4377+ return;
4378+ }
4379+
4380+ int iCurPos = 0;
4381+ if(m_dPlayPos != -1) {
4382+ iCurPos = (int)(m_dPlayPos*m_iNumSamples);
4383+ }
4384+
4385+ if((iCurPos % 2) != 0)
4386+ iCurPos--;
4387+
4388+
4389+ int halfw = m_iInternalBufferSize/2;
4390+
4391+ for(int i=0;i<m_iInternalBufferSize;i++) {
4392+ int thisIndex = iCurPos+2*(i-halfw);
4393+ if(thisIndex >= 0 && (thisIndex+1) < m_iNumSamples) {
4394+ float sampl = (*m_pSampleBuffer)[thisIndex];
4395+ float sampr = (*m_pSampleBuffer)[thisIndex+1];
4396+
4397+ m_pInternalBuffer[i*3+1] = sampl;
4398+ }
4399+
4400+
4401+ }
4402+
4403+ /*
4404+ pPainter->scale(1.0/float(SCALE_TEST),m_iHeight*0.40);
4405+ int halfw = m_iWidth*SCALE_TEST/2;
4406+ for(int i=0;i<m_iWidth*SCALE_TEST;i++) {
4407+ //pPainter->drawLine(QPoint(i,0), QPoint(i, i/sca));
4408+ int thisIndex = iCurPos+2*(i-halfw);
4409+ if(thisIndex >= 0 && (thisIndex+1) < m_iNumSamples) {
4410+ float sampl = (*m_pSampleBuffer)[thisIndex];
4411+ float sampr = (*m_pSampleBuffer)[thisIndex+1];
4412+
4413+ // Old cruft
4414+ //pPainter->drawLine(QPoint(i,m_iHeight/4), QPoint(i,m_iHeight/4+sampl/scaley2));
4415+ //pPainter->drawLine(QPoint(i,-m_iHeight/4), QPoint(i,-m_iHeight/4+sampr/scaley2));
4416+
4417+ // These are decent
4418+ //pPainter->drawLine(QPoint(i,0), QPoint(i, sampl/scaley));
4419+ //pPainter->drawLine(QPoint(i,0), QPoint(i, -sampr/scaley));
4420+
4421+ //m_lines[i] = QLineF(i,-sampr,i,sampl);
4422+ } else {
4423+ //m_lines[i] = QLineF(0,0,0,0);
4424+ }
4425+ }
4426+
4427+ //pPainter->drawLines(m_lines);
4428+
4429+ pPainter->restore();
4430+ */
4431+}
4432+
4433+void GLWaveformRenderer::drawSignalPixmap(QPainter *pPainter) {
4434+
4435+}
4436+
4437+void GLWaveformRenderer::glDraw() {
4438+
4439+ if(m_iWidth == 0 || m_iHeight == 0)
4440+ return;
4441+
4442+ if(m_pSampleBuffer == NULL) {
4443+ fetchWaveformFromTrack();
4444+ if(m_pSampleBuffer != NULL)
4445+ qDebug() << "Received waveform from track";
4446+ }
4447+
4448+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
4449+ glMatrixMode(GL_MODELVIEW);
4450+ glLoadIdentity();
4451+
4452+ gluLookAt(0,0,15,
4453+ 0,0,0,
4454+ 0,1,0);
4455+
4456+ glDrawBuffer(GL_BACK);
4457+
4458+
4459+ glPushMatrix();
4460+
4461+ glScalef(1.0/5.0,m_iHeight/2, 1.0);
4462+
4463+ drawSignalLines();
4464+
4465+ glBegin(GL_LINES);
4466+
4467+ glVertex3f(0,0,1.0);
4468+ glVertex3f(0,1.0,1.0);
4469+
4470+
4471+ glEnd();
4472+
4473+ glEnableClientState(GL_VERTEX_ARRAY);
4474+ glVertexPointer(3, GL_FLOAT, 0, m_pInternalBuffer);
4475+ glDrawArrays(GL_LINE_LOOP,0,m_iInternalBufferSize);
4476+
4477+ glPopMatrix();
4478+
4479+
4480+
4481+ glFlush();
4482+}
4483+
4484+void GLWaveformRenderer::draw(QPainter* pPainter, QPaintEvent *pEvent) {
4485+ pPainter->fillRect(pEvent->rect(), QBrush(bgColor));
4486+ pPainter->setPen(signalColor);
4487+
4488+ if(m_iWidth == 0 || m_iHeight == 0)
4489+ return;
4490+
4491+ //int scaley = 32767*2/ m_iHeight;
4492+ //int scaley2 = 32767*4/ m_iHeight;
4493+ //int sca = m_iWidth*2/ m_iHeight;
4494+
4495+
4496+ if(m_pSampleBuffer == NULL) {
4497+ fetchWaveformFromTrack();
4498+ if(m_pSampleBuffer != NULL)
4499+ qDebug() << "Received waveform from track";
4500+ }
4501+
4502+
4503+
4504+ //drawSignalPixmap(pPainter);
4505+
4506+
4507+
4508+ pPainter->translate(0,m_iHeight/2);
4509+ pPainter->scale(1.0,-1.0);
4510+ //drawSignalLines(pPainter);
4511+
4512+
4513+ // Draw various markers.
4514+ pPainter->setPen(colorMarker);
4515+
4516+ pPainter->drawLine(QLine(0,0,m_iWidth,0));
4517+ pPainter->drawLine(QLine(m_iWidth/2,m_iHeight/2,m_iWidth/2,-m_iHeight/2));
4518+ //pPainter->drawLine(QPoint(-h,0), QPoint(h,0));
4519+ //pPainter->drawLine(QPoint(h,m_iHeight/2), QPoint(h,-m_iHeight/2));
4520+
4521+
4522+}
4523+
4524+void GLWaveformRenderer::newTrack(TrackPointer pTrack) {
4525+ m_pTrack = pTrack;
4526+ m_pSampleBuffer = 0;
4527+ m_iNumSamples = 0;
4528+
4529+}
4530+
4531+int GLWaveformRenderer::getDesiredSecondsToDisplay() {
4532+ return m_iDesiredSecondsToDisplay;
4533+}
4534+
4535+void GLWaveformRenderer::setDesiredSecondsToDisplay(int secondsToDisplay) {
4536+ m_iDesiredSecondsToDisplay = secondsToDisplay;
4537+}
4538
4539=== added file 'mixxx/src/waveform/glwaveformrenderer.h'
4540--- mixxx/src/waveform/glwaveformrenderer.h 1970-01-01 00:00:00 +0000
4541+++ mixxx/src/waveform/glwaveformrenderer.h 2012-06-27 11:06:21 +0000
4542@@ -0,0 +1,59 @@
4543+
4544+#ifndef GLWAVEFORMRENDERER_H
4545+#define GLWAVEFORMRENDERER_H
4546+
4547+#include <QColor>
4548+#include <QPainter>
4549+#include <QPaintEvent>
4550+#include <QVector>
4551+#include <qgl.h>
4552+
4553+#include "defs.h"
4554+#include "trackinfoobject.h"
4555+
4556+class ControlObjectThreadMain;
4557+class QDomNode;
4558+class WaveformRenderBeat;
4559+class ControlObject;
4560+
4561+class GLWaveformRenderer : public QObject {
4562+ Q_OBJECT
4563+public:
4564+ GLWaveformRenderer(const char* group);
4565+ ~GLWaveformRenderer();
4566+
4567+ void resize(int w, int h);
4568+ void draw(QPainter* pPainter, QPaintEvent *pEvent);
4569+ void glDraw();
4570+ void drawSignalLines();
4571+ void drawSignalPixmap(QPainter* p);
4572+ void newTrack(TrackPointer pTrack);
4573+ void setup(QDomNode node);
4574+ void precomputePixmap();
4575+ void setDesiredSecondsToDisplay(int seconds);
4576+ int getDesiredSecondsToDisplay();
4577+
4578+public slots:
4579+ void slotUpdatePlayPos(double playpos);
4580+
4581+private:
4582+ void setupControlObjects();
4583+ bool fetchWaveformFromTrack();
4584+ int m_iWidth, m_iHeight;
4585+ QColor bgColor, signalColor, colorMarker, colorBeat, colorCue;
4586+ int m_iNumSamples, m_iMax, m_iMin;
4587+ double m_dPlayPos;
4588+ QVector<float> *m_pSampleBuffer;
4589+
4590+ ControlObjectThreadMain *m_pPlayPos;
4591+ ControlObject *m_pCOVerticalScale;
4592+ ControlObject *m_pCOVisualResample;
4593+
4594+ int m_iDesiredSecondsToDisplay;
4595+ TrackPointer m_pTrack;
4596+
4597+ GLfloat *m_pInternalBuffer;
4598+ int m_iInternalBufferSize;
4599+};
4600+
4601+#endif
4602
4603=== added file 'mixxx/src/waveform/renderobject.cpp'
4604--- mixxx/src/waveform/renderobject.cpp 1970-01-01 00:00:00 +0000
4605+++ mixxx/src/waveform/renderobject.cpp 2012-06-27 11:06:21 +0000
4606@@ -0,0 +1,12 @@
4607+
4608+#include <QDomNode>
4609+
4610+#include "renderobject.h"
4611+
4612+RenderObject::RenderObject() : QObject() {
4613+
4614+}
4615+
4616+RenderObject::~RenderObject() {
4617+
4618+}
4619
4620=== added file 'mixxx/src/waveform/renderobject.h'
4621--- mixxx/src/waveform/renderobject.h 1970-01-01 00:00:00 +0000
4622+++ mixxx/src/waveform/renderobject.h 2012-06-27 11:06:21 +0000
4623@@ -0,0 +1,27 @@
4624+#ifndef RENDEROBJECT_H
4625+#define RENDEROBJECT_H
4626+
4627+#include <QObject>
4628+#include <QDomNode>
4629+#include <QVector>
4630+
4631+#include "trackinfoobject.h"
4632+
4633+class QDomNode;
4634+class QPainter;
4635+class QPaintEvent;
4636+
4637+class RenderObject : public QObject {
4638+ Q_OBJECT
4639+ public:
4640+ explicit RenderObject();
4641+ virtual ~RenderObject();
4642+ virtual void resize(int w, int h) = 0;
4643+ virtual void setup(QDomNode node) = 0;
4644+ virtual void draw(QPainter *pPainter, QPaintEvent *pEvent,
4645+ QVector<float> *buffer, double playpos,
4646+ double rateAdjust) = 0;
4647+ virtual void newTrack(TrackPointer pTrack) = 0;
4648+};
4649+
4650+#endif
4651
4652=== added file 'mixxx/src/waveform/waveformrenderbackground.cpp'
4653--- mixxx/src/waveform/waveformrenderbackground.cpp 1970-01-01 00:00:00 +0000
4654+++ mixxx/src/waveform/waveformrenderbackground.cpp 2012-06-27 11:06:21 +0000
4655@@ -0,0 +1,70 @@
4656+
4657+#include <QDebug>
4658+#include <QColor>
4659+#include <QDomNode>
4660+#include <QPaintEvent>
4661+#include <QPainter>
4662+#include <QObject>
4663+#include <QVector>
4664+#include <QLine>
4665+#include <QPixmap>
4666+
4667+#include "waveformrenderbackground.h"
4668+#include "waveformrenderer.h"
4669+#include "controlobjectthreadmain.h"
4670+#include "controlobject.h"
4671+#include "widget/wskincolor.h"
4672+#include "widget/wwidget.h"
4673+#include "trackinfoobject.h"
4674+
4675+WaveformRenderBackground::WaveformRenderBackground(const char* group, WaveformRenderer *parent)
4676+ : m_iWidth(0),
4677+ m_iHeight(0),
4678+ m_backgroundPixmap(),
4679+ m_bRepaintBackground(true),
4680+ bgColor(0,0,0) {
4681+}
4682+
4683+WaveformRenderBackground::~WaveformRenderBackground() {
4684+ qDebug() << this << "~WaveformRenderBackground()";
4685+}
4686+
4687+void WaveformRenderBackground::resize(int w, int h) {
4688+ m_iWidth = w;
4689+ m_iHeight = h;
4690+ // Need to repaint the background if we've been resized.
4691+ m_bRepaintBackground = true;
4692+}
4693+
4694+void WaveformRenderBackground::newTrack(TrackPointer pTrack) {
4695+}
4696+
4697+void WaveformRenderBackground::setup(QDomNode node) {
4698+ bgColor.setNamedColor(WWidget::selectNodeQString(node, "BgColor"));
4699+ bgColor = WSkinColor::getCorrectColor(bgColor);
4700+ m_backgroundPixmapPath = WWidget::selectNodeQString(node, "BgPixmap");
4701+ m_bRepaintBackground = true;
4702+}
4703+
4704+void WaveformRenderBackground::draw(QPainter *pPainter, QPaintEvent *pEvent,
4705+ QVector<float> *buffer, double dPlayPos, double rateAdjust) {
4706+ if(m_bRepaintBackground) {
4707+ generatePixmap();
4708+ }
4709+
4710+ pPainter->fillRect(pEvent->rect(), bgColor);
4711+
4712+ // Paint the background pixmap if it exists.
4713+ if (!m_backgroundPixmap.isNull()) {
4714+ pPainter->drawTiledPixmap(pEvent->rect(), m_backgroundPixmap, QPoint(0,0));
4715+ }
4716+}
4717+
4718+void WaveformRenderBackground::generatePixmap() {
4719+ if (m_backgroundPixmapPath != "") {
4720+ m_backgroundPixmap = QPixmap(WWidget::getPath(m_backgroundPixmapPath));
4721+ } else {
4722+ m_backgroundPixmap = QPixmap();
4723+ }
4724+ m_bRepaintBackground = false;
4725+}
4726
4727=== added file 'mixxx/src/waveform/waveformrenderbackground.h'
4728--- mixxx/src/waveform/waveformrenderbackground.h 1970-01-01 00:00:00 +0000
4729+++ mixxx/src/waveform/waveformrenderbackground.h 2012-06-27 11:06:21 +0000
4730@@ -0,0 +1,38 @@
4731+
4732+#ifndef WAVEFORMRENDERBACKGROUND_H
4733+#define WAVEFORMRENDERBACKGROUND_H
4734+
4735+#include <QObject>
4736+#include <QColor>
4737+#include <QVector>
4738+#include <QPixmap>
4739+
4740+#include "renderobject.h"
4741+
4742+class QDomNode;
4743+class QPainter;
4744+class QPaintEvent;
4745+
4746+class WaveformRenderer;
4747+
4748+class WaveformRenderBackground : public RenderObject {
4749+ Q_OBJECT
4750+ public:
4751+ WaveformRenderBackground(const char *group, WaveformRenderer *parent);
4752+ virtual ~WaveformRenderBackground();
4753+
4754+ void resize(int w, int h);
4755+ void setup(QDomNode node);
4756+ void draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer, double playPos, double rateAdjust);
4757+ void newTrack(TrackPointer pTrack);
4758+
4759+ private:
4760+ void generatePixmap();
4761+ int m_iWidth, m_iHeight;
4762+ QColor bgColor;
4763+ QPixmap m_backgroundPixmap;
4764+ QString m_backgroundPixmapPath;
4765+ bool m_bRepaintBackground;
4766+};
4767+
4768+#endif
4769
4770=== added file 'mixxx/src/waveform/waveformrenderbeat.cpp'
4771--- mixxx/src/waveform/waveformrenderbeat.cpp 1970-01-01 00:00:00 +0000
4772+++ mixxx/src/waveform/waveformrenderbeat.cpp 2012-06-27 11:06:21 +0000
4773@@ -0,0 +1,183 @@
4774+
4775+#include <QDebug>
4776+#include <QColor>
4777+#include <QDomNode>
4778+#include <QPaintEvent>
4779+#include <QPainter>
4780+#include <QObject>
4781+#include <QVector>
4782+
4783+#include "waveformrenderbeat.h"
4784+#include "waveformrenderer.h"
4785+#include "controlobjectthreadmain.h"
4786+#include "controlobject.h"
4787+#include "widget/wskincolor.h"
4788+#include "widget/wwidget.h"
4789+#include "trackinfoobject.h"
4790+#include "track/beats.h"
4791+
4792+
4793+WaveformRenderBeat::WaveformRenderBeat(const char* group, WaveformRenderer *parent)
4794+ : m_pParent(parent),
4795+ m_pTrackSamples(NULL),
4796+ m_pTrack(),
4797+ m_iWidth(0),
4798+ m_iHeight(0),
4799+ m_dSamplesPerPixel(-1),
4800+ m_dSamplesPerDownsample(-1),
4801+ m_iNumSamples(0),
4802+ m_iSampleRate(0),
4803+ m_bBeatActive(false) {
4804+ m_pTrackSamples = new ControlObjectThreadMain(
4805+ ControlObject::getControl(ConfigKey(group,"track_samples")));
4806+ slotUpdateTrackSamples(m_pTrackSamples->get());
4807+ connect(m_pTrackSamples, SIGNAL(valueChanged(double)),
4808+ this, SLOT(slotUpdateTrackSamples(double)));
4809+
4810+ m_pTrackSampleRate = new ControlObjectThreadMain(
4811+ ControlObject::getControl(ConfigKey(group,"track_samplerate")));
4812+ slotUpdateTrackSampleRate(m_pTrackSampleRate->get());
4813+ connect(m_pTrackSampleRate, SIGNAL(valueChanged(double)),
4814+ this, SLOT(slotUpdateTrackSampleRate(double)));
4815+
4816+ m_pBeatActive = new ControlObjectThreadMain(
4817+ ControlObject::getControl(ConfigKey(group,"beat_active")));
4818+ slotUpdateBeatActive(m_pBeatActive->get());
4819+ connect(m_pBeatActive, SIGNAL(valueChanged(double)),
4820+ this, SLOT(slotUpdateBeatActive(double)));
4821+}
4822+
4823+WaveformRenderBeat::~WaveformRenderBeat() {
4824+ delete m_pTrackSamples;
4825+ delete m_pBeatActive;
4826+ qDebug() << this << "~WaveformRenderBeat()";
4827+}
4828+
4829+void WaveformRenderBeat::slotUpdateTrackSamples(double samples) {
4830+ //qDebug() << "WaveformRenderBeat :: samples = " << int(samples);
4831+ m_iNumSamples = static_cast<int>(samples);
4832+}
4833+
4834+void WaveformRenderBeat::slotUpdateTrackSampleRate(double sampleRate) {
4835+
4836+
4837+ // f = z * m * n
4838+ double m = m_pParent->getSubpixelsPerPixel();
4839+ double f = sampleRate;
4840+ double z = m_pParent->getPixelsPerSecond();
4841+ double n = f / (m*z);
4842+
4843+ m_iSampleRate = static_cast<int>(sampleRate);
4844+ m_dSamplesPerDownsample = n;
4845+ m_dSamplesPerPixel = f/z;
4846+
4847+ //qDebug() << "WaveformRenderBeat :: sampleRate = " << int(sampleRate)
4848+ // << "samplesPerDownsample" << m_dSamplesPerDownsample
4849+ // << "samplesPerPixel" << m_dSamplesPerPixel;
4850+}
4851+
4852+void WaveformRenderBeat::slotUpdateBeatActive(double beatActive) {
4853+ m_bBeatActive = beatActive > 0;
4854+}
4855+
4856+void WaveformRenderBeat::resize(int w, int h) {
4857+ m_iWidth = w;
4858+ m_iHeight = h;
4859+}
4860+
4861+void WaveformRenderBeat::newTrack(TrackPointer pTrack) {
4862+ m_pTrack = pTrack;
4863+}
4864+
4865+void WaveformRenderBeat::setup(QDomNode node) {
4866+ colorMarks.setNamedColor(WWidget::selectNodeQString(node, "BeatColor"));
4867+ colorMarks = WSkinColor::getCorrectColor(colorMarks);
4868+
4869+ colorHighlight = Qt::black;
4870+ QString highlight = WWidget::selectNodeQString(node, "BeatHighlightColor");
4871+ if (highlight != "") {
4872+ colorHighlight.setNamedColor(highlight);
4873+ }
4874+ colorHighlight = WSkinColor::getCorrectColor(colorHighlight);
4875+}
4876+
4877+void WaveformRenderBeat::draw(QPainter *pPainter, QPaintEvent *event,
4878+ QVector<float> *buffer, double dPlayPos, double rateAdjust) {
4879+ slotUpdateTrackSamples(m_pTrackSamples->get());
4880+
4881+ if(m_iSampleRate <= 0 || m_iNumSamples == 0)
4882+ return;
4883+
4884+ if(buffer == NULL)
4885+ return;
4886+
4887+ BeatsPointer pBeats = m_pTrack->getBeats();
4888+ if (!pBeats)
4889+ return;
4890+
4891+ int iCurPos = (int)(dPlayPos * m_iNumSamples);
4892+ if(iCurPos % 2 != 0)
4893+ iCurPos--;
4894+
4895+ // iCurPos is the current sample being processed the current pixel
4896+ // p, with respect to iCurPos is in the screen if it is less than
4897+ // halfw from iCurPos. A sample is a beat if it satisifes the following:
4898+
4899+ // for b beats per minute, that means b/60 beats per seconds, or 60/b seconds per beat.
4900+ // with a sample rate of f (generally 44khz),
4901+ // 60f/b = samples per beat
4902+
4903+ // Therefore, sample s is a beat if it satisfies s % 60f/b == 0.
4904+ // where s is a /mono/ sample
4905+
4906+ double subpixelsPerPixel = m_pParent->getSubpixelsPerPixel()*(1.0+rateAdjust);
4907+
4908+ QPen marksPen(colorMarks);
4909+ marksPen.setWidth(subpixelsPerPixel*1.5);
4910+ QPen highlightPen(colorHighlight);
4911+ highlightPen.setWidth(subpixelsPerPixel*1.5);
4912+
4913+ pPainter->save();
4914+ pPainter->scale(1.0/subpixelsPerPixel,1.0);
4915+
4916+ pPainter->setPen(marksPen);
4917+
4918+ double subpixelWidth = m_iWidth * subpixelsPerPixel;
4919+ double subpixelHalfWidth = subpixelWidth / 2.0;
4920+ double halfh = m_iHeight/2;
4921+
4922+ // basePos and endPos are in samples
4923+ double basePos = iCurPos - m_dSamplesPerPixel * m_iWidth * (1.0+rateAdjust);
4924+ double endPos = basePos + (2 * m_iWidth) * m_dSamplesPerPixel * (1.0+rateAdjust);
4925+
4926+
4927+ m_beatList.clear();
4928+ pBeats->findBeats(basePos, endPos, &m_beatList);
4929+
4930+ bool reset = false;
4931+ foreach (double curPos, m_beatList) {
4932+ if (curPos < 0)
4933+ continue;
4934+
4935+ // i relative to the current play position in subpixels
4936+ double i = (((curPos) - iCurPos)/2)/m_dSamplesPerDownsample;
4937+
4938+ // If a beat is active, highlight the marker.
4939+ if(m_bBeatActive && abs(i) < 20) {
4940+ pPainter->setPen(highlightPen);
4941+ reset = true;
4942+ }
4943+
4944+ // Translate from -subpixelHalfWidth..subpixelHalfwidth to 0..subpixelWidth
4945+ i += subpixelHalfWidth;
4946+
4947+ pPainter->drawLine(QLineF(i,halfh,i,-halfh));
4948+
4949+ if(reset) {
4950+ pPainter->setPen(marksPen);
4951+ reset = false;
4952+ }
4953+ }
4954+
4955+ pPainter->restore();
4956+}
4957
4958=== added file 'mixxx/src/waveform/waveformrenderbeat.h'
4959--- mixxx/src/waveform/waveformrenderbeat.h 1970-01-01 00:00:00 +0000
4960+++ mixxx/src/waveform/waveformrenderbeat.h 2012-06-27 11:06:21 +0000
4961@@ -0,0 +1,53 @@
4962+
4963+#ifndef WAVEFORMRENDERBEAT_H
4964+#define WAVEFORMRENDERBEAT_H
4965+
4966+#include <QObject>
4967+#include <QColor>
4968+#include <QVector>
4969+
4970+#include "renderobject.h"
4971+
4972+class QDomNode;
4973+class QPainter;
4974+class QPaintEvent;
4975+
4976+class ControlObjectThreadMain;
4977+class WaveformRenderer;
4978+class SoundSourceProxy;
4979+
4980+class WaveformRenderBeat : public RenderObject {
4981+ Q_OBJECT
4982+ public:
4983+ WaveformRenderBeat(const char *group, WaveformRenderer *parent);
4984+ virtual ~WaveformRenderBeat();
4985+
4986+ void resize(int w, int h);
4987+ void setup(QDomNode node);
4988+ void draw(QPainter *pPainter, QPaintEvent *event, QVector<float> *buffer,
4989+ double playPos, double rateAdjust);
4990+ void newTrack(TrackPointer pTrack);
4991+
4992+ private slots:
4993+ void slotUpdateTrackSamples(double samples);
4994+ void slotUpdateBeatActive(double beatActive);
4995+ void slotUpdateTrackSampleRate(double sampleRate);
4996+
4997+ private:
4998+ WaveformRenderer *m_pParent;
4999+ ControlObjectThreadMain* m_pTrackSamples;
5000+ ControlObjectThreadMain *m_pTrackSampleRate;
The diff has been truncated for viewing.