summaryrefslogtreecommitdiffstats
path: root/src/engine/bpmcontrol.h
blob: a715179ee09bbb452e6c8777fd6fa31ffa66ce1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#ifndef BPMCONTROL_H
#define BPMCONTROL_H

#include <gtest/gtest_prod.h>

#include "control/controlobject.h"
#include "engine/enginecontrol.h"
#include "engine/sync/syncable.h"
#include "util/tapfilter.h"

class ControlObject;
class ControlLinPotmeter;
class ControlProxy;
class ControlPushButton;
class EngineBuffer;
class SyncControl;

class BpmControl : public EngineControl {
    Q_OBJECT

  public:
    BpmControl(QString group, UserSettingsPointer pConfig);
    ~BpmControl() override;

    double getBpm() const;
    double getLocalBpm() const { return m_pLocalBpm ? m_pLocalBpm->get() : 0.0; }
    // When in master sync mode, ratecontrol calls calcSyncedRate to figure out
    // how fast the track should play back.  The returned rate is usually just
    // the correct pitch to match bpms.  The usertweak argument represents
    // how much the user is nudging the pitch to get two tracks into sync, and
    // that value is added to the rate by bpmcontrol.  The rate may be
    // further adjusted if bpmcontrol discovers that the tracks have fallen
    // out of sync.
    double calcSyncedRate(double userTweak);
    // Get the phase offset from the specified position.
    double getNearestPositionInPhase(double dThisPosition, bool respectLoops, bool playing);
    double getPhaseOffset(double dThisPosition);
    double getBeatDistance(double dThisPosition) const;

    void setTargetBeatDistance(double beatDistance);
    void setInstantaneousBpm(double instantaneousBpm);
    void resetSyncAdjustment();
    double updateLocalBpm();
    double updateBeatDistance();

    void collectFeatures(GroupFeatureState* pGroupFeatures) const;

    // Calculates contextual information about beats: the previous beat, the
    // next beat, the current beat length, and the beat ratio (how far dPosition
    // lies within the current beat). Returns false if a previous or next beat
    // does not exist. NULL arguments are safe and ignored.
    static bool getBeatContext(const BeatsPointer& pBeats,
                               const double dPosition,
                               double* dpPrevBeat,
                               double* dpNextBeat,
                               double* dpBeatLength,
                               double* dpBeatPercentage);

    // Alternative version that works if the next and previous beat positions
    // are already known.
    static bool getBeatContextNoLookup(
                               const double dPosition,
                               const double dPrevBeat,
                               const double dNextBeat,
                               double* dpBeatLength,
                               double* dpBeatPercentage);

    // Returns the shortest change in percentage needed to achieve
    // target_percentage.
    // Example: shortestPercentageChange(0.99, 0.01) == 0.02
    static double shortestPercentageChange(const double& current_percentage,
                                           const double& target_percentage);
    void trackLoaded(TrackPointer pNewTrack) override;

  private slots:
    void slotFileBpmChanged(double);
    void slotAdjustBeatsFaster(double);
    void slotAdjustBeatsSlower(double);
    void slotTranslateBeatsEarlier(double);
    void slotTranslateBeatsLater(double);
    void slotControlBeatSync(double);
    void slotControlBeatSyncPhase(double);
    void slotControlBeatSyncTempo(double);
    void slotTapFilter(double,int);
    void slotBpmTap(double);
    void slotUpdateRateSlider(double v = 0.0);
    void slotUpdateEngineBpm(double v = 0.0);
    void slotUpdatedTrackBeats();
    void slotBeatsTranslate(double);
    void slotBeatsTranslateMatchAlignment(double);

  private:
    SyncMode getSyncMode() const {
        return syncModeFromDouble(m_pSyncMode->get());
    }
    bool syncTempo();
    double calcSyncAdjustment(double my_percentage, bool userTweakingSync);
    double calcRateRatio() const;

    friend class SyncControl;

    // ControlObjects that come from EngineBuffer
    ControlProxy* m_pPlayButton;
    QAtomicInt m_oldPlayButton;
    ControlProxy* m_pReverseButton;
    ControlProxy* m_pRateSlider;
    ControlObject* m_pQuantize;
    ControlProxy* m_pRateRange;
    ControlProxy* m_pRateDir;

    // ControlObjects that come from QuantizeControl
    QScopedPointer<ControlProxy> m_pNextBeat;
    QScopedPointer<ControlProxy> m_pPrevBeat;
    QScopedPointer<ControlProxy> m_pClosestBeat;

    // ControlObjects that come from LoopingControl
    ControlProxy* m_pLoopEnabled;
    ControlProxy* m_pLoopStartPosition;
    ControlProxy* m_pLoopEndPosition;

    // The current loaded file's detected BPM
    ControlObject* m_pFileBpm;
    // The average bpm around the current playposition;
    ControlObject* m_pLocalBpm;
    ControlPushButton* m_pAdjustBeatsFaster;
    ControlPushButton* m_pAdjustBeatsSlower;
    ControlPushButton* m_pTranslateBeatsEarlier;
    ControlPushButton* m_pTranslateBeatsLater;

    // The current effective BPM of the engine
    ControlLinPotmeter* m_pEngineBpm;

    // Used for bpm tapping from GUI and MIDI
    ControlPushButton* m_pButtonTap;

    // Button for sync'ing with the other EngineBuffer
    ControlPushButton* m_pButtonSync;
    ControlPushButton* m_pButtonSyncPhase;
    ControlPushButton* m_pButtonSyncTempo;

    // Button that translates the beats so the nearest beat is on the current
    // playposition.
    ControlPushButton* m_pTranslateBeats;
    // Button that translates beats to match another playing deck
    ControlPushButton* m_pBeatsTranslateMatchAlignment;

    ControlProxy* m_pThisBeatDistance;
    ControlValueAtomic<double> m_dSyncTargetBeatDistance;
    ControlValueAtomic<double> m_dUserOffset;
    QAtomicInt m_resetSyncAdjustment;
    ControlProxy* m_pSyncMode;

    TapFilter m_tapFilter; // threadsave

    // used in the engine thread only
    double m_dSyncInstantaneousBpm;
    double m_dLastSyncAdjustment;

    // objects below are written from an engine worker thread
    TrackPointer m_pTrack;
    BeatsPointer m_pBeats;

    const QString m_sGroup;

    FRIEND_TEST(EngineSyncTest, UserTweakBeatDistance);
};


#endif // BPMCONTROL_H