diff options
author | Uwe Klotz <uklotz@mixxx.org> | 2019-09-29 09:53:02 +0200 |
---|---|---|
committer | Uwe Klotz <uklotz@mixxx.org> | 2019-09-29 09:53:02 +0200 |
commit | 1d882d8b5022005a120e23f20496f8cd9795d45c (patch) | |
tree | 5089fa8478860f26ec9e0a3f91f25269285bc76e | |
parent | a1fdea2aef545ee0bd9f25027f0d66f936325cf8 (diff) | |
parent | 5998ee24e30361b3d14907d899e191a0aee16a3b (diff) |
Merge branch 'master' of git@github.com:mixxxdj/mixxx.git into new-signals-slots-syntax-library
# Conflicts:
# src/library/library.cpp
# src/library/scanner/libraryscanner.cpp
63 files changed, 1720 insertions, 382 deletions
diff --git a/build/depends.py b/build/depends.py index a127271a46..3cb0980d8b 100644 --- a/build/depends.py +++ b/build/depends.py @@ -975,6 +975,7 @@ class MixxxCore(Feature): "src/database/schemamanager.cpp", "src/library/trackcollection.cpp", + "src/library/externaltrackcollection.cpp", "src/library/basesqltablemodel.cpp", "src/library/basetrackcache.cpp", "src/library/columncache.cpp", diff --git a/default.nix b/default.nix index 1b674e0041..165206dc65 100644 --- a/default.nix +++ b/default.nix @@ -1,4 +1,7 @@ -{ nixroot ? (import <nixpkgs> {}) }: +{ nixroot ? (import <nixpkgs> {}) +, defaultLv2Plugins ? false +, lv2Plugins ? [] +}: let inherit (nixroot) stdenv pkgs lib chromaprint fftw flac libid3tag libmad libopus libshout libsndfile lilv libusb1 libvorbis libebur128 pkgconfig portaudio portmidi protobuf qt5 glib @@ -42,7 +45,7 @@ let inherit (nixroot) stdenv pkgs lib shell-run = nixroot.writeShellScriptBin "run" '' BUILDDIR=$(ls -1 -d -t lin64_build lin_build | head -1) - $BUILDDIR/mixxx --settingsPath ./devsettings/ --resourcePath ./res "$@" + /usr/bin/env LV2_PATH=${lib.makeSearchPathOutput "lib" "lib/lv2" allLv2Plugins}:$LV2_PATH $BUILDDIR/mixxx --settingsPath ./devsettings/ --resourcePath ./res "$@" ''; shell-debug = nixroot.writeShellScriptBin "debug" '' @@ -50,6 +53,11 @@ let inherit (nixroot) stdenv pkgs lib gdb --args $BUILDDIR/mixxx --settingsPath ./devsettings/ --resourcePath ./res "$@" ''; + allLv2Plugins = lv2Plugins ++ (if defaultLv2Plugins then [ + nixroot.x42-plugins nixroot.zam-plugins nixroot.rkrlv2 nixroot.mod-distortion + nixroot.infamousPlugins nixroot.artyFX + ] else []); + in stdenv.mkDerivation rec { name = "mixxx-${version}"; # reading the version from git output is very hard to do without wasting lots of diskspace and runtime @@ -83,7 +91,7 @@ in stdenv.mkDerivation rec { libusb1 libvorbis libebur128 pkgconfig portaudio portmidi protobuf qt5.full rubberband scons sqlite taglib soundtouch vamp.vampSDK opusfile upower hidapi ccache git glib x11 libGLU lilv lame lv2 makeWrapper qt5.qtbase - ]; + ] ++ allLv2Plugins; sconsFlags = [ "build=debug" @@ -102,7 +110,7 @@ in stdenv.mkDerivation rec { installPhase = '' runHook preInstall scons $sconsFlags "prefix=$out" install - wrapProgram $out/bin/mixxx --suffix QT_PLUGIN_PATH : ${qt5.qtbase}/${qt5.qtbase.qtPluginPrefix} --set QTDIR ${qt5.full} + wrapProgram $out/bin/mixxx --suffix QT_PLUGIN_PATH : ${qt5.qtbase}/${qt5.qtbase.qtPluginPrefix} --set QTDIR ${qt5.full} --prefix LV2_PATH : ${lib.makeSearchPath "lib/lv2" allLv2Plugins} runHook postInstall ''; diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 4dc43be1e9..7bb5c00957 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -533,6 +533,34 @@ } } }, + forEachComponentContainer: function (operation, recursive) { + if (typeof operation !== 'function') { + print('ERROR: ComponentContainer.forEachComponentContainer requires a function argument'); + return; + } + if (recursive === undefined) { recursive = true; } + + var that = this; + var applyOperationTo = function (obj) { + if (obj instanceof ComponentContainer) { + operation.call(that, obj); + + if (recursive) { + obj.forEachComponentContainer(operation); + } + } else if (Array.isArray(obj)) { + obj.forEach(function (element) { + applyOperationTo(element); + }); + } + }; + + for (var memberName in this) { + if (this.hasOwnProperty(memberName)) { + applyOperationTo(this[memberName]); + } + } + }, reconnectComponents: function (operation, recursive) { this.forEachComponent(function (component) { component.disconnect(); @@ -545,6 +573,7 @@ }, isShifted: false, shift: function () { + // Shift direct child Components this.forEachComponent(function (component) { // Controls for push type Buttons depend on getting reset to 0 when the // Button is released for correct behavior. If there is a skin button @@ -568,11 +597,18 @@ } component.shift(); } - // Set isShifted for child ComponentContainers forEachComponent is iterating through recursively - this.isShifted = true; - }); + }, false); + + // Shift child ComponentContainers + this.forEachComponentContainer(function (container) { + container.shift(); + }, false); + + // Set isShifted for each ComponentContainer recursively + this.isShifted = true; }, unshift: function () { + // Unshift direct child Components this.forEachComponent(function (component) { // Refer to comment in ComponentContainer.shift() above for explanation if (typeof component.unshift === 'function') { @@ -588,9 +624,15 @@ } component.unshift(); } - // Set isShifted for child ComponentContainers forEachComponent is iterating through recursively - this.isShifted = false; - }); + }, false); + + // Unshift child ComponentContainers + this.forEachComponentContainer(function (container) { + container.unshift(); + }, false); + + // Unset isShifted for each ComponentContainer recursively + this.isShifted = false; }, applyLayer: function (newLayer, reconnectComponents) { if (reconnectComponents !== false) { diff --git a/src/engine/controls/loopingcontrol.cpp b/src/engine/controls/loopingcontrol.cpp index 02f9e10fb4..245201df76 100644 --- a/src/engine/controls/loopingcontrol.cpp +++ b/src/engine/controls/loopingcontrol.cpp @@ -53,7 +53,7 @@ LoopingControl::LoopingControl(QString group, m_loopSamples.setValue(m_oldLoopSamples); m_currentSample.setValue(0.0); m_pActiveBeatLoop = NULL; - + m_pRateControl = NULL; //Create loop-in, loop-out, loop-exit, and reloop/exit ControlObjects m_pLoopInButton = new ControlPushButton(ConfigKey(group, "loop_in")); connect(m_pLoopInButton, &ControlObject::valueChanged, @@ -682,6 +682,10 @@ void LoopingControl::setLoopOutToCurrentPosition() { m_loopSamples.setValue(loopSamples); } +void LoopingControl::setRateControl(RateControl* rateControl) { + m_pRateControl = rateControl; +} + void LoopingControl::slotLoopOut(double pressed) { if (m_pTrack == nullptr) { return; @@ -1060,16 +1064,19 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable newloopSamples.start = currentSample; } } else { - // loop_in is set to the previous beat if quantize is on. The - // closest beat might be ahead of play position which would cause a seek. - // TODO: If in reverse, should probably choose nextBeat. + // loop_in is set to the closest beat if quantize is on and the loop size is >= 1 beat. + // The closest beat might be ahead of play position and will cause a catching loop. double prevBeat; double nextBeat; pBeats->findPrevNextBeats(currentSample, &prevBeat, &nextBeat); if (m_pQuantizeEnabled->toBool() && prevBeat != -1) { + double beatLength = nextBeat - prevBeat; + double loopLength = beatLength * beats; + + double closestBeat = pBeats->findClosestBeat(currentSample); if (beats >= 1.0) { - newloopSamples.start = prevBeat; + newloopSamples.start = closestBeat; } else { // In case of beat length less then 1 beat: // (| - beats, ^ - current track's position): @@ -1078,15 +1085,29 @@ void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint, bool enable // // If we press 1/2 beatloop we want loop from 50% to 100%, // If I press 1/4 beatloop, we want loop from 50% to 75% etc - double beat_len = nextBeat - prevBeat; - double loops_per_beat = 1.0 / beats; - double beat_pos = currentSample - prevBeat; - int beat_frac = - static_cast<int>(floor((beat_pos / beat_len) * - loops_per_beat)); - newloopSamples.start = prevBeat + beat_len / loops_per_beat * beat_frac; + double samplesSinceLastBeat = currentSample - prevBeat; + + // find the previous beat fraction and check if the current position is closer to this or the next one + // place the new loop start to the closer one + double previousFractionBeat = prevBeat + floor(samplesSinceLastBeat / loopLength) * loopLength; + double samplesSinceLastFractionBeat = currentSample - previousFractionBeat; + + if (samplesSinceLastFractionBeat <= (loopLength / 2.0)) { + newloopSamples.start = previousFractionBeat; + } else { + newloopSamples.start = previousFractionBeat + loopLength; + } } + // If running reverse, move the loop one loop size to the left. + // Thus, the loops end will be closest to the current position + bool reverse = false; + if (m_pRateControl != NULL) { + reverse = m_pRateControl->isReverseButtonPressed(); + } + if (reverse) { + newloopSamples.start -= loopLength; + } } else { newloopSamples.start = currentSample; } diff --git a/src/engine/controls/loopingcontrol.h b/src/engine/controls/loopingcontrol.h index 55d4f0c15f..6be5fdb78b 100644 --- a/src/engine/controls/loopingcontrol.h +++ b/src/engine/controls/loopingcontrol.h @@ -8,11 +8,12 @@ #include <QObject> #include <QStack> -#include "preferences/usersettings.h" +#include "control/controlvalue.h" #include "engine/controls/enginecontrol.h" -#include "track/track.h" +#include "engine/controls/ratecontrol.h" +#include "preferences/usersettings.h" #include "track/beats.h" -#include "control/controlvalue.h" +#include "track/track.h" #define MINIMUM_AUDIBLE_LOOP_SIZE 300 // In samples @@ -50,7 +51,7 @@ class LoopingControl : public EngineControl { double getSyncPositionInsideLoop(double dRequestedPlaypos, double dSyncedPlayPos); void notifySeek(double dNewPlaypos) override; - + void setRateControl(RateControl* rateControl); bool isLoopingEnabled(); public slots: @@ -129,6 +130,7 @@ class LoopingControl : public EngineControl { ControlPushButton* m_pLoopHalveButton; ControlPushButton* m_pLoopDoubleButton; ControlObject* m_pSlipEnabled; + RateControl* m_pRateControl; ControlObject* m_pPlayButton; bool m_bLoopingEnabled; diff --git a/src/engine/controls/ratecontrol.cpp b/src/engine/controls/ratecontrol.cpp index 75c7327b10..5ee5af58ff 100644 --- a/src/engine/controls/ratecontrol.cpp +++ b/src/engine/controls/ratecontrol.cpp @@ -556,3 +556,10 @@ void RateControl::resetRateTemp(void) void RateControl::notifySeek(double playPos) { m_pScratchController->notifySeek(playPos); } + +bool RateControl::isReverseButtonPressed() { + if (m_pReverseButton) { + return m_pReverseButton->toBool(); + } + return false; +} diff --git a/src/engine/controls/ratecontrol.h b/src/engine/controls/ratecontrol.h index 635ae15b2e..a53dfd383f 100644 --- a/src/engine/controls/ratecontrol.h +++ b/src/engine/controls/ratecontrol.h @@ -78,6 +78,7 @@ public: static void setRateRampSensitivity(int); static int getRateRampSensitivity(); void notifySeek(double dNewPlaypos) override; + bool isReverseButtonPressed(); public slots: void slotReverseRollActivate(double); diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 2c10c3bfe8..5ba47c24a5 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -180,14 +180,13 @@ EngineBuffer::EngineBuffer(const QString& group, UserSettingsPointer pConfig, // quantization (alignment) of loop in/out positions and (hot)cues with // beats. QuantizeControl* quantize_control = new QuantizeControl(group, pConfig); + addControl(quantize_control); + m_pQuantize = ControlObject::getControl(ConfigKey(group, "quantize")); // Create the Loop Controller m_pLoopingControl = new LoopingControl(group, pConfig); addControl(m_pLoopingControl); - addControl(quantize_control); - m_pQuantize = ControlObject::getControl(ConfigKey(group, "quantize")); - m_pEngineSync = pMixingEngine->getEngineSync(); m_pSyncControl = new SyncControl(group, pConfig, pChannel, m_pEngineSync); @@ -198,9 +197,12 @@ EngineBuffer::EngineBuffer(const QString& group, UserSettingsPointer pConfig, addControl(m_pVinylControlControl); #endif + // Create the Rate Controller m_pRateControl = new RateControl(group, pConfig); // Add the Rate Controller addControl(m_pRateControl); + // Looping Control needs Rate Control for Reverse Button + m_pLoopingControl->setRateControl(m_pRateControl); // Create the BPM Controller m_pBpmControl = new BpmControl(group, pConfig); @@ -532,6 +534,10 @@ TrackPointer EngineBuffer::getLoadedTrack() const { return m_pCurrentTrack; } +bool EngineBuffer::isReverse() { + return m_reverse_old; +} + void EngineBuffer::ejectTrack() { // clear track values in any case, this may fix Bug #1450424 //qDebug() << "EngineBuffer::ejectTrack()"; diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index 46735d16fa..394794225f 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -146,6 +146,8 @@ class EngineBuffer : public EngineObject { bool getQueuedSeekPosition(double* pSeekPosition); TrackPointer getLoadedTrack() const; + bool isReverse(); + double getExactPlayPos(); double getVisualPlayPos(); double getTrackSamples(); diff --git a/src/library/dao/directorydao.cpp b/src/library/dao/directorydao.cpp index c490ef959d..15338680c7 100644 --- a/src/library/dao/directorydao.cpp +++ b/src/library/dao/directorydao.cpp @@ -14,7 +14,6 @@ int DirectoryDAO::addDirectory(const QString& newDir) { // Do nothing if the dir to add is a child of a directory that is already in // the db. - ScopedTransaction transaction(m_database); QStringList dirs = getDirs(); QString childDir; QString parentDir; @@ -46,7 +45,6 @@ int DirectoryDAO::addDirectory(const QString& newDir) { LOG_FAILED_QUERY(query) << "Adding new dir (" % newDir % ") failed."; return SQL_ERROR; } - transaction.commit(); return ALL_FINE; } @@ -80,13 +78,12 @@ int DirectoryDAO::removeDirectory(const QString& dir) { } -QSet<TrackId> DirectoryDAO::relocateDirectory(const QString& oldFolder, +QList<TrackRef> DirectoryDAO::relocateDirectory(const QString& oldFolder, const QString& newFolder) { // TODO(rryan): This method could use error reporting. It can fail in // mysterious ways for example if a track in the oldFolder also has a zombie // track location in newFolder then the replace query will fail because the // location column becomes non-unique. - ScopedTransaction transaction(m_database); QSqlQuery query(m_database); query.prepare("UPDATE " % DIRECTORYDAO_TABLE % " SET " % DIRECTORYDAO_DIR % "=:newFolder WHERE " % DIRECTORYDAO_DIR % " = :oldFolder"); @@ -95,7 +92,7 @@ QSet<TrackId> DirectoryDAO::relocateDirectory(const QString& oldFolder, if (!query.exec()) { LOG_FAILED_QUERY(query) << "could not relocate directory" << oldFolder << "to" << newFolder; - return QSet<TrackId>(); + return {}; } // on Windows the absolute path starts with the drive name @@ -113,35 +110,32 @@ QSet<TrackId> DirectoryDAO::relocateDirectory(const QString& oldFolder, .arg(startsWithOldFolder, kSqlLikeMatchAll)); if (!query.exec()) { LOG_FAILED_QUERY(query) << "could not relocate path of tracks"; - return QSet<TrackId>(); + return {}; } - QSet<TrackId> trackIds; - QList<int> loc_ids; - QStringList old_locs; + QList<DbId> loc_ids; + QList<TrackRef> trackRefs; while (query.next()) { - trackIds.insert(TrackId(query.value(0))); - loc_ids.append(query.value(1).toInt()); - old_locs.append(query.value(2).toString()); + loc_ids.append(DbId(query.value(1).toInt())); + trackRefs.append(TrackRef::fromFileInfo(query.value(2).toString(), TrackId(query.value(0)))); } QString replacement = "UPDATE track_locations SET location = :newloc " "WHERE id = :id"; query.prepare(replacement); for (int i = 0; i < loc_ids.size(); ++i) { - QString newloc = old_locs.at(i); + QString newloc = trackRefs.at(i).getLocation(); newloc.replace(0, oldFolder.size(), newFolder); query.bindValue("newloc", newloc); - query.bindValue("id", loc_ids.at(i)); + query.bindValue("id", loc_ids.at(i).toVariant()); if (!query.exec()) { LOG_FAILED_QUERY(query) << "could not relocate path of tracks"; - return QSet<TrackId>(); + return {}; } } - qDebug() << "Relocated tracks:" << trackIds.size(); - transaction.commit(); - return trackIds; + qDebug() << "Relocated tracks:" << trackRefs.size(); + return trackRefs; } QStringList DirectoryDAO::getDirs() { diff --git a/src/library/dao/directorydao.h b/src/library/dao/directorydao.h index 827bd4a703..6892021a96 100644 --- a/src/library/dao/directorydao.h +++ b/src/library/dao/directorydao.h @@ -21,7 +21,7 @@ class DirectoryDAO : public DAO { |