~shanx-shashank/mixxx/effects_ladspa

« back to all changes in this revision

Viewing changes to mixxx/src/engine/bpmcontrol.cpp

  • Committer: shanxS
  • Date: 2013-06-16 07:42:19 UTC
  • Revision ID: shanx.shashank@gmail.com-20130616074219-wszmk0slwfa1z61q
Init Repository.
Starting with GUI of lp:~shanx-shashank/mixxx/effects_parametricEq as base

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// bpmcontrol.cpp
 
2
// Created 7/5/2009 by RJ Ryan (rryan@mit.edu)
 
3
 
 
4
#include <QtDebug>
 
5
 
 
6
#include "controlobject.h"
 
7
#include "controlpushbutton.h"
 
8
 
 
9
#include "engine/enginebuffer.h"
 
10
#include "engine/enginestate.h"
 
11
#include "engine/bpmcontrol.h"
 
12
 
 
13
const int minBpm = 30;
 
14
const int maxInterval = (int)(1000.*(60./(CSAMPLE)minBpm));
 
15
const int filterLength = 5;
 
16
 
 
17
BpmControl::BpmControl(const char* _group,
 
18
                       ConfigObject<ConfigValue>* _config,
 
19
                       EngineState* pEngineState) :
 
20
        EngineControl(_group, _config),
 
21
        m_tapFilter(this, filterLength, maxInterval) {
 
22
    m_pTrackWatcher = pEngineState->getTrackManager()->createTrackWatcher();
 
23
    connect(m_pTrackWatcher, SIGNAL(beatsUpdated()),
 
24
            this, SLOT(slotUpdatedTrackBeats()),
 
25
            Qt::DirectConnection);
 
26
 
 
27
    CallbackControlManager* pControlManager = pEngineState->getControlManager();
 
28
    m_pPlayButton = pControlManager->getControl(ConfigKey(_group, "play"));
 
29
    m_pRateSlider = pControlManager->getControl(ConfigKey(_group, "rate"));
 
30
    connect(m_pRateSlider, SIGNAL(valueChanged(double)),
 
31
            this, SLOT(slotRateChanged(double)),
 
32
            Qt::DirectConnection);
 
33
    connect(m_pRateSlider, SIGNAL(valueChangedFromEngine(double)),
 
34
            this, SLOT(slotRateChanged(double)),
 
35
            Qt::DirectConnection);
 
36
 
 
37
    m_pRateRange = pControlManager->getControl(ConfigKey(_group, "rateRange"));
 
38
    connect(m_pRateRange, SIGNAL(valueChanged(double)),
 
39
            this, SLOT(slotRateChanged(double)),
 
40
            Qt::DirectConnection);
 
41
    connect(m_pRateRange, SIGNAL(valueChangedFromEngine(double)),
 
42
            this, SLOT(slotRateChanged(double)),
 
43
            Qt::DirectConnection);
 
44
 
 
45
    m_pRateDir = pControlManager->getControl(ConfigKey(_group, "rate_dir"));
 
46
    connect(m_pRateDir, SIGNAL(valueChanged(double)),
 
47
            this, SLOT(slotRateChanged(double)),
 
48
            Qt::DirectConnection);
 
49
    connect(m_pRateDir, SIGNAL(valueChangedFromEngine(double)),
 
50
            this, SLOT(slotRateChanged(double)),
 
51
            Qt::DirectConnection);
 
52
 
 
53
    m_pFileBpm = pControlManager->addControl(
 
54
        new ControlObject(ConfigKey(_group, "file_bpm")), 1);
 
55
    connect(m_pFileBpm, SIGNAL(valueChanged(double)),
 
56
            this, SLOT(slotFileBpmChanged(double)),
 
57
            Qt::DirectConnection);
 
58
 
 
59
    m_pEngineBpm = pControlManager->addControl(
 
60
        new ControlObject(ConfigKey(_group, "bpm")), 1);
 
61
    connect(m_pEngineBpm, SIGNAL(valueChanged(double)),
 
62
            this, SLOT(slotSetEngineBpm(double)),
 
63
            Qt::DirectConnection);
 
64
 
 
65
    m_pButtonTap = pControlManager->addControl(
 
66
        new ControlPushButton(ConfigKey(_group, "bpm_tap")), 1);
 
67
    connect(m_pButtonTap, SIGNAL(valueChanged(double)),
 
68
            this, SLOT(slotBpmTap(double)),
 
69
            Qt::DirectConnection);
 
70
 
 
71
    // Beat sync (scale buffer tempo relative to tempo of other buffer)
 
72
    m_pButtonSync = pControlManager->addControl(
 
73
        new ControlPushButton(ConfigKey(_group, "beatsync")), 1);
 
74
    connect(m_pButtonSync, SIGNAL(valueChanged(double)),
 
75
            this, SLOT(slotControlBeatSync(double)),
 
76
            Qt::DirectConnection);
 
77
 
 
78
    m_pButtonSyncPhase = pControlManager->addControl(
 
79
        new ControlPushButton(ConfigKey(_group, "beatsync_phase")), 1);
 
80
    connect(m_pButtonSyncPhase, SIGNAL(valueChanged(double)),
 
81
            this, SLOT(slotControlBeatSyncPhase(double)),
 
82
            Qt::DirectConnection);
 
83
 
 
84
    m_pButtonSyncTempo = pControlManager->addControl(
 
85
        new ControlPushButton(ConfigKey(_group, "beatsync_tempo")), 1);
 
86
    connect(m_pButtonSyncTempo, SIGNAL(valueChanged(double)),
 
87
            this, SLOT(slotControlBeatSyncTempo(double)),
 
88
            Qt::DirectConnection);
 
89
 
 
90
    m_pTranslateBeats = pControlManager->addControl(
 
91
        new ControlPushButton(ConfigKey(_group, "beats_translate_curpos")), 1);
 
92
    connect(m_pTranslateBeats, SIGNAL(valueChanged(double)),
 
93
            this, SLOT(slotBeatsTranslate(double)),
 
94
            Qt::DirectConnection);
 
95
 
 
96
    connect(&m_tapFilter, SIGNAL(tapped(double,int)),
 
97
            this, SLOT(slotTapFilter(double,int)),
 
98
            Qt::DirectConnection);
 
99
}
 
100
 
 
101
BpmControl::~BpmControl() {
 
102
    delete m_pEngineBpm;
 
103
    delete m_pFileBpm;
 
104
    delete m_pButtonSync;
 
105
    delete m_pButtonSyncTempo;
 
106
    delete m_pButtonSyncPhase;
 
107
    delete m_pButtonTap;
 
108
    delete m_pTranslateBeats;
 
109
    delete m_pTrackWatcher;
 
110
}
 
111
 
 
112
double BpmControl::getBpm() {
 
113
    return m_pEngineBpm->get();
 
114
}
 
115
 
 
116
void BpmControl::slotFileBpmChanged(double bpm) {
 
117
    //qDebug() << this << "slotFileBpmChanged" << bpm;
 
118
    // Adjust the file-bpm with the current setting of the rate to get the
 
119
    // engine BPM.
 
120
    double dRate = 1.0 + m_pRateDir->get() * m_pRateRange->get() * m_pRateSlider->get();
 
121
    m_pEngineBpm->set(bpm * dRate);
 
122
}
 
123
 
 
124
void BpmControl::slotSetEngineBpm(double bpm) {
 
125
    double filebpm = m_pFileBpm->get();
 
126
 
 
127
    if (filebpm != 0.0) {
 
128
        double newRate = bpm / filebpm - 1.0f;
 
129
        newRate = math_max(-1.0f, math_min(1.0f, newRate));
 
130
        m_pRateSlider->set(newRate * m_pRateDir->get());
 
131
    }
 
132
}
 
133
 
 
134
void BpmControl::slotBpmTap(double v) {
 
135
    if (v > 0) {
 
136
        m_tapFilter.tap();
 
137
    }
 
138
}
 
139
 
 
140
void BpmControl::slotTapFilter(double averageLength, int numSamples) {
 
141
    // averageLength is the average interval in milliseconds tapped over
 
142
    // numSamples samples.  Have to convert to BPM now:
 
143
 
 
144
    if (averageLength <= 0)
 
145
        return;
 
146
 
 
147
    if (numSamples < 4)
 
148
        return;
 
149
 
 
150
    // (60 seconds per minute) * (1000 milliseconds per second) / (X millis per
 
151
    // beat) = Y beats/minute
 
152
    double averageBpm = 60.0 * 1000.0 / averageLength;
 
153
    m_pFileBpm->set(averageBpm);
 
154
    slotFileBpmChanged(averageBpm);
 
155
}
 
156
 
 
157
void BpmControl::slotControlBeatSyncPhase(double v) {
 
158
    if (!v)
 
159
        return;
 
160
    syncPhase();
 
161
}
 
162
 
 
163
void BpmControl::slotControlBeatSyncTempo(double v) {
 
164
    if (!v)
 
165
        return;
 
166
    syncTempo();
 
167
}
 
168
 
 
169
void BpmControl::slotControlBeatSync(double v) {
 
170
    if (!v)
 
171
        return;
 
172
 
 
173
    // If the player is playing, and adjusting its tempo succeeded, adjust its
 
174
    // phase so that it plays in sync.
 
175
    if (syncTempo() && m_pPlayButton->get() > 0) {
 
176
        syncPhase();
 
177
    }
 
178
}
 
179
 
 
180
bool BpmControl::syncTempo() {
 
181
    EngineBuffer* pOtherEngineBuffer = getOtherEngineBuffer();
 
182
 
 
183
    if(!pOtherEngineBuffer)
 
184
        return false;
 
185
 
 
186
    double fThisBpm  = m_pEngineBpm->get();
 
187
    //double fThisRate = m_pRateDir->get() * m_pRateSlider->get() * m_pRateRange->get();
 
188
    double fThisFileBpm = m_pFileBpm->get();
 
189
 
 
190
    double fOtherBpm = pOtherEngineBuffer->getBpm();
 
191
    double fOtherRate = pOtherEngineBuffer->getRate();
 
192
    double fOtherFileBpm = fOtherBpm / (1.0 + fOtherRate);
 
193
 
 
194
    //qDebug() << "this" << "bpm" << fThisBpm << "filebpm" << fThisFileBpm << "rate" << fThisRate;
 
195
    //qDebug() << "other" << "bpm" << fOtherBpm << "filebpm" << fOtherFileBpm << "rate" << fOtherRate;
 
196
 
 
197
    ////////////////////////////////////////////////////////////////////////////
 
198
    // Rough proof of how syncing works -- rryan 3/2011
 
199
    // ------------------------------------------------
 
200
    //
 
201
    // Let this and other denote this deck versus the sync-target deck.
 
202
    //
 
203
    // The goal is for this deck's effective BPM to equal the other decks.
 
204
    //
 
205
    // thisBpm = otherBpm
 
206
    //
 
207
    // The overall rate is the product of range, direction, and scale plus 1:
 
208
    //
 
209
    // rate = 1.0 + rateDir * rateRange * rateScale
 
210
    //
 
211
    // An effective BPM is the file-bpm times the rate:
 
212
    //
 
213
    // bpm = fileBpm * rate
 
214
    //
 
215
    // So our goal is to tweak thisRate such that this equation is true:
 
216
    //
 
217
    // thisFileBpm * (1.0 + thisRate) = otherFileBpm * (1.0 + otherRate)
 
218
    //
 
219
    // so rearrange this equation in terms of thisRate:
 
220
    //
 
221
    // thisRate = (otherFileBpm * (1.0 + otherRate)) / thisFileBpm - 1.0
 
222
    //
 
223
    // So the new rateScale to set is:
 
224
    //
 
225
    // thisRateScale = ((otherFileBpm * (1.0 + otherRate)) / thisFileBpm - 1.0) / (thisRateDir * thisRateRange)
 
226
 
 
227
    if (fOtherBpm > 0.0 && fThisBpm > 0.0) {
 
228
        // The desired rate is the other decks effective rate divided by this
 
229
        // deck's file BPM. This gives us the playback rate that will produe an
 
230
        // effective BPM equivalent to the other decks.
 
231
        double fDesiredRate = fOtherBpm / fThisFileBpm;
 
232
 
 
233
        // Test if this buffer's bpm is the double of the other one, and adjust
 
234
        // the rate scale. I believe this is intended to account for our BPM
 
235
        // algorithm sometimes finding double or half BPMs. This avoids drastic
 
236
        // scales.
 
237
 
 
238
        float fFileBpmDelta = fabs(fThisFileBpm-fOtherFileBpm);
 
239
        if (fabs(fThisFileBpm*2.0 - fOtherFileBpm) < fFileBpmDelta) {
 
240
            fDesiredRate /= 2.0;
 
241
        } else if (fabs(fThisFileBpm - 2.0*fOtherFileBpm) < fFileBpmDelta) {
 
242
            fDesiredRate *= 2.0;
 
243
        }
 
244
 
 
245
        // Subtract the base 1.0, now fDesiredRate is the percentage
 
246
        // increase/decrease in playback rate, not the playback rate.
 
247
        fDesiredRate -= 1.0;
 
248
 
 
249
        // Ensure the rate is within resonable boundaries. Remember, this is the
 
250
        // percent to scale the rate, not the rate itself. If fDesiredRate was -1,
 
251
        // that would mean the deck would be completely stopped. If fDesiredRate
 
252
        // is 1, that means it is playing at 2x speed. This limit enforces that
 
253
        // we are scaled between 0.5x and 2x.
 
254
        if (fDesiredRate < 1.0 && fDesiredRate > -0.5)
 
255
        {
 
256
            // Adjust the rateScale. We have to divide by the range and
 
257
            // direction to get the correct rateScale.
 
258
            fDesiredRate = fDesiredRate/(m_pRateRange->get() * m_pRateDir->get());
 
259
 
 
260
            // And finally, set the slider
 
261
            m_pRateSlider->set(fDesiredRate);
 
262
            return true;
 
263
        }
 
264
    }
 
265
    return false;
 
266
}
 
267
 
 
268
bool BpmControl::syncPhase() {
 
269
    EngineBuffer* pOtherEngineBuffer = getOtherEngineBuffer();
 
270
    TrackPointer otherTrack = pOtherEngineBuffer->getLoadedTrack();
 
271
    BeatsPointer otherBeats = otherTrack ? otherTrack->getBeats() : BeatsPointer();
 
272
 
 
273
    // If either track does not have beats, then we can't adjust the phase.
 
274
    if (!m_pBeats || !otherBeats) {
 
275
        return false;
 
276
    }
 
277
 
 
278
    // Get the file BPM of each song.
 
279
    //double dThisBpm = m_pBeats->getBpm();
 
280
    //double dOtherBpm = ControlObject::getControl(
 
281
    //ConfigKey(pOtherEngineBuffer->getGroup(), "file_bpm"))->get();
 
282
 
 
283
    // Get the current position of both decks
 
284
    double dThisPosition = getCurrentSample();
 
285
    double dOtherLength = ControlObject::getControl(
 
286
        ConfigKey(pOtherEngineBuffer->getGroup(), "track_samples"))->get();
 
287
    double dOtherPosition = dOtherLength * ControlObject::getControl(
 
288
        ConfigKey(pOtherEngineBuffer->getGroup(), "visual_playposition"))->get();
 
289
 
 
290
    double dThisPrevBeat = m_pBeats->findPrevBeat(dThisPosition);
 
291
    double dThisNextBeat = m_pBeats->findNextBeat(dThisPosition);
 
292
 
 
293
    if (dThisPrevBeat == -1 || dThisNextBeat == -1) {
 
294
        return false;
 
295
    }
 
296
 
 
297
    // Protect against the case where we are sitting exactly on the beat.
 
298
    if (dThisPrevBeat == dThisNextBeat) {
 
299
        dThisNextBeat = m_pBeats->findNthBeat(dThisPosition, 2);
 
300
    }
 
301
 
 
302
    double dOtherPrevBeat = otherBeats->findPrevBeat(dOtherPosition);
 
303
    double dOtherNextBeat = otherBeats->findNextBeat(dOtherPosition);
 
304
 
 
305
    if (dOtherPrevBeat == -1 || dOtherNextBeat == -1) {
 
306
        return false;
 
307
    }
 
308
 
 
309
    // Protect against the case where we are sitting exactly on the beat.
 
310
    if (dOtherPrevBeat == dOtherNextBeat) {
 
311
        dOtherNextBeat = otherBeats->findNthBeat(dOtherPosition, 2);
 
312
    }
 
313
 
 
314
    double dThisBeatLength = fabs(dThisNextBeat - dThisPrevBeat);
 
315
    double dOtherBeatLength = fabs(dOtherNextBeat - dOtherPrevBeat);
 
316
    double dOtherBeatFraction = (dOtherPosition - dOtherPrevBeat) / dOtherBeatLength;
 
317
 
 
318
    double dNewPlaypos;
 
319
    bool this_near_next = dThisNextBeat - dThisPosition <= dThisPosition - dThisPrevBeat;
 
320
    bool other_near_next = dOtherNextBeat - dOtherPosition <= dOtherPosition - dOtherPrevBeat;
 
321
 
 
322
    // We want our beat fraction to be identical to theirs.
 
323
 
 
324
    // If the two tracks have similar alignment, adjust phase is straight-
 
325
    // forward.  Use the same fraction for both beats, starting from the previous
 
326
    // beat.  But if This track is nearer to the next beat and the Other track
 
327
    // is nearer to the previous beat, use This Next beat as the starting point
 
328
    // for the phase. (ie, we pushed the sync button late).  If This track
 
329
    // is nearer to the previous beat, but the Other track is nearer to the
 
330
    // next beat, we pushed the sync button early so use the double-previous
 
331
    // beat as the basis for the adjustment.
 
332
    //
 
333
    // This makes way more sense when you're actually mixing.
 
334
    //
 
335
    // TODO(XXX) Revisit this logic once we move away from tempo-locked,
 
336
    // infinite beatgrids because the assumption that findNthBeat(-2) always
 
337
    // works will be wrong then.
 
338
 
 
339
    if (this_near_next == other_near_next) {
 
340
        dNewPlaypos = dThisPrevBeat + dOtherBeatFraction * dThisBeatLength;
 
341
    } else if (this_near_next && !other_near_next) {
 
342
        dNewPlaypos = dThisNextBeat + dOtherBeatFraction * dThisBeatLength;
 
343
    } else {  //!this_near_next && other_near_next
 
344
        dThisPrevBeat = m_pBeats->findNthBeat(dThisPosition, -2);
 
345
        dNewPlaypos = dThisPrevBeat + dOtherBeatFraction * dThisBeatLength;
 
346
    }
 
347
 
 
348
    emit(seekAbs(dNewPlaypos));
 
349
    return true;
 
350
}
 
351
 
 
352
void BpmControl::slotRateChanged(double) {
 
353
    double dFileBpm = m_pFileBpm->get();
 
354
    slotFileBpmChanged(dFileBpm);
 
355
}
 
356
 
 
357
void BpmControl::trackLoaded(TrackPointer pTrack) {
 
358
    if (m_pTrack) {
 
359
        trackUnloaded(m_pTrack);
 
360
    }
 
361
 
 
362
    if (pTrack) {
 
363
        m_pTrack = pTrack;
 
364
        m_pBeats = m_pTrack->getBeats();
 
365
        m_pTrackWatcher->watchTrack(m_pTrack);
 
366
    }
 
367
}
 
368
 
 
369
void BpmControl::trackUnloaded(TrackPointer pTrack) {
 
370
    if (m_pTrack) {
 
371
        m_pTrackWatcher->unwatchTrack(m_pTrack);
 
372
    }
 
373
    m_pTrack.clear();
 
374
    m_pBeats.clear();
 
375
}
 
376
 
 
377
void BpmControl::slotUpdatedTrackBeats() {
 
378
    //qDebug() << "BpmControl::slotUpdatedTrackBeats()";
 
379
    if (m_pTrack) {
 
380
        m_pBeats = m_pTrack->getBeats();
 
381
    }
 
382
}
 
383
 
 
384
void BpmControl::slotBeatsTranslate(double v) {
 
385
    if (v > 0 && m_pBeats && (m_pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) {
 
386
        double currentSample = getCurrentSample();
 
387
        double closestBeat = m_pBeats->findClosestBeat(currentSample);
 
388
        int delta = currentSample - closestBeat;
 
389
        if (delta % 2 != 0) {
 
390
            delta--;
 
391
        }
 
392
        m_pBeats->translate(delta);
 
393
    }
 
394
}