Ejemplo n.º 1
0
void CueControl::updateIndicators() {
    // No need for mutex lock because we are only touching COs.
    double cueMode = m_pCueMode->get();

    if (cueMode == CUE_MODE_DENON || cueMode == CUE_MODE_NUMARK) {
        // Cue button is only lit at cue point
        bool playing = m_pPlayButton->get() > 0;
        if (isTrackAtCue()) {
            // at cue point
            if (!playing) {
                m_pCueIndicator->setBlinkValue(ControlIndicator::ON);
                m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF);
            }
        } else {
            m_pCueIndicator->setBlinkValue(ControlIndicator::OFF);
            if (!playing) {
                if (getCurrentSample() < getTotalSamples() && cueMode != CUE_MODE_NUMARK) {
                    // Play will move cue point
                    m_pPlayIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_500MS);
                } else {
                    // At track end
                    m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF);
                }
            }
        }
    } else {
        // Here we have CUE_MODE_PIONEER or CUE_MODE_MIXXX
        // default to Pioneer mode
        if (!m_bPreviewing) {
            bool playing = m_pPlayButton->get() > 0;
            if (!playing) {
                if (!isTrackAtCue()) {
                    if (getCurrentSample() < getTotalSamples()) {
                        if (cueMode == CUE_MODE_MIXXX) {
                            // in Mixxx mode Cue Button is flashing slow if CUE will move Cue point
                            m_pCueIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_500MS);
                        } else {
                            // in Pioneer mode Cue Button is flashing fast if CUE will move Cue point
                            m_pCueIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_250MS);
                        }
                    } else {
                        // At track end
                        m_pCueIndicator->setBlinkValue(ControlIndicator::OFF);
                    }
                } else if (m_pCuePoint->get() != -1) {
                    // Next Press is preview
                    m_pCueIndicator->setBlinkValue(ControlIndicator::ON);
                }
            }
        }
    }
}
Ejemplo n.º 2
0
void LoopingControl::slotLoopMove(double beats) {
    if (!m_pTrack || !m_pBeats) {
        return;
    }

    double dPosition = getCurrentSample();
    double dBeatLength;
    if (BpmControl::getBeatContext(m_pBeats, dPosition,
                                   NULL, NULL, &dBeatLength, NULL)) {
        int old_loop_in = m_iLoopStartSample;
        int old_loop_out = m_iLoopEndSample;
        int new_loop_in = m_iLoopStartSample + (beats * dBeatLength);
        int new_loop_out = m_iLoopEndSample + (beats * dBeatLength);
        // Should we reject any shift that goes out of bounds?

        m_iLoopStartSample = new_loop_in;
        if (m_pActiveBeatLoop) {
            // Ugly hack -- slotBeatLoop takes "true" to mean "keep starting
            // point".  It gets that in-point from m_iLoopStartSample,
            // which we just changed so that the loop actually shifts.
            slotBeatLoop(m_pActiveBeatLoop->getSize(), true);
        } else {
            m_pCOLoopStartPosition->set(new_loop_in);
            m_iLoopEndSample = new_loop_out;
            m_pCOLoopEndPosition->set(new_loop_out);
        }
        seekInsideAdjustedLoop(old_loop_in, old_loop_out,
                               new_loop_in, new_loop_out);
    }
}
Ejemplo n.º 3
0
void CueControl::hotcueSet(HotcueControl* pControl, double v) {
    //qDebug() << "CueControl::hotcueSet" << v;

    if (!v)
        return;

    QMutexLocker lock(&m_mutex);
    if (!m_pLoadedTrack)
        return;

    int hotcue = pControl->getHotcueNumber();
    detachCue(hotcue);
    Cue* pCue = m_pLoadedTrack->addCue();
    double cuePosition =
            (m_pQuantizeEnabled->get() > 0.0 && m_pClosestBeat->get() != -1) ?
            floorf(m_pClosestBeat->get()) : floorf(getCurrentSample());
    if (!even(cuePosition))
        cuePosition--;
    pCue->setPosition(cuePosition);
    pCue->setHotCue(hotcue);
    pCue->setLabel("");
    pCue->setType(Cue::CUE);
    // TODO(XXX) deal with spurious signals
    attachCue(pCue, hotcue);

    // If quantize is enabled and we are not playing, jump to the cue point
    // since it's not necessarily where we currently are. TODO(XXX) is this
    // potentially invalid for vinyl control?
    bool playing = m_pPlayButton->get() > 0;
    if (!playing && m_pQuantizeEnabled->get() > 0.0) {
        lock.unlock();  // prevent deadlock.
        // Enginebuffer will quantize more exactly than we can.
        seekAbs(cuePosition);
    }
}
Ejemplo n.º 4
0
TEST_F(CueControlTest, SeekOnLoadDefault_NoCue) {
    m_pSeekOnLoadMode->slotSet(SEEK_ON_LOAD_DEFAULT);

    TrackPointer pTrack = createTestTrack();

    loadTrack(pTrack);

    EXPECT_DOUBLE_EQ(-1.0, m_pCuePoint->get());
    EXPECT_DOUBLE_EQ(0.0, getCurrentSample());

    // Set cue and check if track is seeked to it.
    pTrack->setCuePoint(CuePosition(200.0, Cue::MANUAL));
    ProcessBuffer();

    EXPECT_DOUBLE_EQ(200.0, m_pCuePoint->get());
    EXPECT_DOUBLE_EQ(200.0, getCurrentSample());
}
Ejemplo n.º 5
0
void CueControl::cueCDJ(double v) {
    // This is how Pioneer cue buttons work:
    // If pressed while playing, stop playback and go to cue.
    // If pressed while stopped and at cue, play while pressed.
    // If pressed while stopped and not at cue, set new cue point.
    // If play is pressed while holding cue, the deck is now playing. (Handled in playFromCuePreview().)

    QMutexLocker lock(&m_mutex);
    bool playing = (m_pPlayButton->get() == 1.0);

    if (v) {
        if (playing || getCurrentSample() >= getTotalSamples()) {
            // Jump to cue when playing or when at end position

            // Just in case.
            m_bPreviewing = false;
            m_pPlayButton->set(0.0);

            // Need to unlock before emitting any signals to prevent deadlock.
            lock.unlock();

            seekAbs(m_pCuePoint->get());
        } else if (isTrackAtCue()) {
            // pause at cue point
            m_bPreviewing = true;
            m_pPlayButton->set(1.0);
        } else {
            // Pause not at cue point and not at end position
            cueSet(v);
            // Just in case.
            m_bPreviewing = false;

            // If quantize is enabled, jump to the cue point since it's not
            // necessarily where we currently are
            if (m_pQuantizeEnabled->get() > 0.0) {
                lock.unlock();  // prevent deadlock.
                // Enginebuffer will quantize more exactly than we can.
                seekAbs(m_pCuePoint->get());
            }
        }
    } else if (m_bPreviewing) {
        m_bPreviewing = false;
        m_pPlayButton->set(0.0);

        // Need to unlock before emitting any signals to prevent deadlock.
        lock.unlock();

        seekAbs(m_pCuePoint->get());
    }
    // indicator may flash because the delayed adoption of seekAbs
    // Correct the Indicator set via play
    if (m_pLoadedTrack && !playing) {
        m_pCueIndicator->setBlinkValue(ControlIndicator::ON);
    } else {
        m_pCueIndicator->setBlinkValue(ControlIndicator::OFF);
    }
}
Ejemplo n.º 6
0
TEST_F(CueControlTest, SeekOnLoadMainCue) {
    m_pSeekOnLoadMode->slotSet(SEEK_ON_LOAD_MAIN_CUE);

    TrackPointer pTrack = createTestTrack();
    pTrack->setCuePoint(CuePosition(100.0, Cue::MANUAL));

    loadTrack(pTrack);

    EXPECT_DOUBLE_EQ(100.0, m_pCuePoint->get());
    EXPECT_DOUBLE_EQ(100.0, getCurrentSample());

    // Move cue and check if track is following it.
    pTrack->setCuePoint(CuePosition(200.0, Cue::MANUAL));
    ProcessBuffer();

    EXPECT_DOUBLE_EQ(200.0, m_pCuePoint->get());
    EXPECT_DOUBLE_EQ(200.0, getCurrentSample());
}
Ejemplo n.º 7
0
void BpmControl::slotBeatsTranslateMatchAlignment(double v) {
    if (v > 0 && m_pBeats && (m_pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) {
        // Must reset the user offset *before* calling getPhaseOffset(),
        // otherwise it will always return 0 if master sync is active.
        m_dUserOffset = 0.0;

        double offset = getPhaseOffset(getCurrentSample());
        m_pBeats->translate(-offset);
    }
}
Ejemplo n.º 8
0
void CueControl::cueCDJ(double v) {
    /* This is how CDJ cue buttons work:
     * If pressed while playing, stop playback and go to cue.
     * If pressed while stopped and at cue, play while pressed.
     * If pressed while stopped and not at cue, set new cue point.
     * If play is pressed while holding cue, the deck is now playing. (Handled in playFromCuePreview().)
     */

    QMutexLocker lock(&m_mutex);
    bool playing = (m_pPlayButton->get() == 1.0);
    double cuePoint = m_pCuePoint->get();

    if (v) {
        if (playing) {
            m_pPlayButton->set(0.0);

            // Just in case.
            m_bPreviewing = false;

            // Need to unlock before emitting any signals to prevent deadlock.
            lock.unlock();

            seekAbs(cuePoint);
        } else {
            if (fabs(getCurrentSample() - m_pCuePoint->get()) < 1.0f) {
                m_pPlayButton->set(1.0);
                m_bPreviewing = true;
            } else {
                cueSet(v);
                // Just in case.
                m_bPreviewing = false;

                // If quantize is enabled, jump to the cue point since it's not
                // necessarily where we currently are
                if (m_pQuantizeEnabled->get() > 0.0) {
                    lock.unlock();  // prevent deadlock.
                    seekAbs(m_pCuePoint->get());
                }
            }
        }
    } else if (m_bPreviewing) {
        m_pPlayButton->set(0.0);
        m_bPreviewing = false;

        // Need to unlock before emitting any signals to prevent deadlock.
        lock.unlock();

        seekAbs(cuePoint);
    }
    else {
        // Re-trigger the play button value so controllers get the correct one
        // after playFromCuePreview() changes it.
        m_pPlayButton->set(m_pPlayButton->get());
    }
}
Ejemplo n.º 9
0
void BpmControl::slotBeatsTranslate(double v) {
    if (v > 0 && m_pBeats && (m_pBeats->getCapabilities() & Beats::BEATSCAP_TRANSLATE)) {
        double currentSample = getCurrentSample();
        double closestBeat = m_pBeats->findClosestBeat(currentSample);
        int delta = currentSample - closestBeat;
        if (delta % 2 != 0) {
            delta--;
        }
        m_pBeats->translate(delta);
    }
}
Ejemplo n.º 10
0
TEST_F(CueControlTest, SeekOnLoadZeroPos) {
    m_pSeekOnLoadMode->slotSet(SEEK_ON_LOAD_ZERO_POS);

    TrackPointer pTrack = createTestTrack();
    pTrack->setCuePoint(CuePosition(100.0, Cue::MANUAL));

    loadTrack(pTrack);

    EXPECT_DOUBLE_EQ(100.0, m_pCuePoint->get());
    EXPECT_DOUBLE_EQ(0.0, getCurrentSample());
}
Ejemplo n.º 11
0
TEST_F(CueControlTest, SeekOnLoadIntroCue) {
    m_pSeekOnLoadMode->slotSet(SEEK_ON_LOAD_INTRO_CUE);

    TrackPointer pTrack = createTestTrack();
    auto pIntro = pTrack->createAndAddCue();
    pIntro->setType(Cue::INTRO);
    pIntro->setSource(Cue::MANUAL);
    pIntro->setPosition(200.0);

    loadTrack(pTrack);

    EXPECT_DOUBLE_EQ(200.0, m_pIntroStartPosition->get());
    EXPECT_DOUBLE_EQ(200.0, getCurrentSample());

    // Move cue and check if track is following it.
    pIntro->setPosition(400.0);
    ProcessBuffer();

    EXPECT_DOUBLE_EQ(400.0, m_pIntroStartPosition->get());
    EXPECT_DOUBLE_EQ(400.0, getCurrentSample());
}
Ejemplo n.º 12
0
void CueControl::cueSet(double v) {
    if (!v)
        return;

    QMutexLocker lock(&m_mutex);
    double cue = (m_pQuantizeEnabled->get() > 0.0 && m_pClosestBeat->get() != -1) ?
            floorf(m_pClosestBeat->get()) : floorf(getCurrentSample());
    if (!even(cue))
        cue--;
    m_pCuePoint->set(cue);
    saveCuePoint(cue);
}
Ejemplo n.º 13
0
void LoopingControl::slotBeatJump(double beats) {
    if (!m_pTrack || !m_pBeats) {
        return;
    }

    double dPosition = getCurrentSample();
    double dBeatLength;
    if (BpmControl::getBeatContext(m_pBeats, dPosition,
                                   NULL, NULL, &dBeatLength, NULL)) {
        seekAbs(dPosition + beats * dBeatLength);
    }
}
Ejemplo n.º 14
0
TEST_F(CueControlTest, SeekOnLoadDefault_CueRecallDisabled) {
    m_pSeekOnLoadMode->slotSet(SEEK_ON_LOAD_DEFAULT);

    // Note: CueRecall uses inverse logic (0 means enabled).
    config()->set(ConfigKey("[Controls]", "CueRecall"), ConfigValue(1));

    TrackPointer pTrack = createTestTrack();
    pTrack->setCuePoint(CuePosition(100.0, Cue::MANUAL));

    loadTrack(pTrack);

    EXPECT_DOUBLE_EQ(100.0, m_pCuePoint->get());
    EXPECT_DOUBLE_EQ(0.0, getCurrentSample());
}
Ejemplo n.º 15
0
// Moves the cue point to current position or to closest beat in case
// quantize is enabled
void CueControl::cueSet(double v) {
    if (!v)
        return;

    QMutexLocker lock(&m_mutex);
    double closestBeat = m_pClosestBeat->get();
    double cue = (m_pQuantizeEnabled->get() > 0.0 && closestBeat != -1) ?
            closestBeat : getCurrentSample();
    m_pCuePoint->set(cue);
    TrackPointer pLoadedTrack = m_pLoadedTrack;
    lock.unlock();

    // Store cue point in loaded track
    if (pLoadedTrack) {
        pLoadedTrack->setCuePoint(cue);
    }
}
Ejemplo n.º 16
0
double BpmControl::updateLocalBpm() {
    double prev_local_bpm = m_pLocalBpm->get();
    double local_bpm = 0;
    if (m_pBeats) {
        local_bpm = m_pBeats->getBpmAroundPosition(getCurrentSample(),
                                                   kLocalBpmSpan);
        if (local_bpm == -1) {
            local_bpm = m_pFileBpm->get();
        }
    } else {
        local_bpm = m_pFileBpm->get();
    }
    if (local_bpm != prev_local_bpm) {
        m_pLocalBpm->set(local_bpm);
        slotUpdateEngineBpm();
    }
    return local_bpm;
}
Ejemplo n.º 17
0
// Moves the cue point to current position or to closest beat in case
// quantize is enabled
void CueControl::cueSet(double v) {
    if (!v)
        return;

    QMutexLocker lock(&m_mutex);
    double cue = (m_pQuantizeEnabled->get() > 0.0 && m_pClosestBeat->get() != -1) ?
            floor(m_pClosestBeat->get()) : floor(getCurrentSample());
    if (!even(static_cast<int>(cue))) {
        cue--;
    }
    m_pCuePoint->set(cue);
    TrackPointer pLoadedTrack = m_pLoadedTrack;
    lock.unlock();

    // Store cue point in loaded track
    if (pLoadedTrack) {
        pLoadedTrack->setCuePoint(cue);
    }
}
Ejemplo n.º 18
0
double BpmControl::calcSyncedRate(double userTweak) {
    double rate = 1.0;
    // Don't know what to do if there's no bpm.
    if (m_pLocalBpm->get() != 0.0) {
        rate = m_dSyncInstantaneousBpm / m_pLocalBpm->get();
    }

    // If we are not quantized, or there are no beats, or we're master,
    // or we're in reverse, just return the rate as-is.
    if (!m_pQuantize->get() || getSyncMode() == SYNC_MASTER ||
            m_pBeats == NULL || m_pReverseButton->get()) {
        m_resetSyncAdjustment = true;
        return rate + userTweak;
    }

    // Now we need to get our beat distance so we can figure out how
    // out of phase we are.
    double dThisPosition = getCurrentSample();
    double dBeatLength;
    double my_percentage;
    if (!BpmControl::getBeatContextNoLookup(dThisPosition,
                                            m_pPrevBeat->get(), m_pNextBeat->get(),
                                            &dBeatLength, &my_percentage)) {
        m_resetSyncAdjustment = true;
        return rate + userTweak;
    }

    // Now that we have our beat distance we can also check how large the
    // current loop is.  If we are in a <1 beat loop, don't worry about offset.
    const bool loop_enabled = m_pLoopEnabled->toBool();
    const double loop_size = (m_pLoopEndPosition->get() -
                              m_pLoopStartPosition->get()) /
                              dBeatLength;
    if (loop_enabled && loop_size < 1.0 && loop_size > 0) {
        m_resetSyncAdjustment = true;
        return rate + userTweak;
    }

    // Now we have all we need to calculate the sync adjustment if any.
    double adjustment = calcSyncAdjustment(my_percentage, userTweak != 0.0);
    return (rate + userTweak) * adjustment;
}
Ejemplo n.º 19
0
void CueControl::cuePlay(double v) {
    // This is how CUP button works:
    // If freely playing (i.e. playing and platter NOT being touched), press to go to cue and stop.
    // If not freely playing (i.e. stopped or platter IS being touched), press to go to cue and stop.
    // On release, start playing from cue point.


    QMutexLocker lock(&m_mutex);
    const auto freely_playing = m_pPlay->toBool() && !getEngineBuffer()->getScratching();

    // pressed
    if (v) {
        if (freely_playing) {
            m_bPreviewing = false;
            m_pPlay->set(0.0);

            // Need to unlock before emitting any signals to prevent deadlock.
            lock.unlock();

            seekAbs(m_pCuePoint->get());
        } else if (!isTrackAtCue() && getCurrentSample() <= getTotalSamples()) {
            // Pause not at cue point and not at end position
            cueSet(v);
            // Just in case.
            m_bPreviewing = false;
            m_pPlay->set(0.0);
            // If quantize is enabled, jump to the cue point since it's not
            // necessarily where we currently are
            if (m_pQuantizeEnabled->get() > 0.0) {
                lock.unlock();  // prevent deadlock.
                // Enginebuffer will quantize more exactly than we can.
                seekAbs(m_pCuePoint->get());
            }
        }
    } else if (isTrackAtCue()){
        m_bPreviewing = false;
        m_pPlay->set(1.0);
        lock.unlock();

    }
}
Ejemplo n.º 20
0
void BpmControl::collectFeatures(GroupFeatureState* pGroupFeatures) const {
    double fileBpm = m_pFileBpm->get();
    if (fileBpm > 0) {
        pGroupFeatures->has_file_bpm = true;
        pGroupFeatures->file_bpm = fileBpm;
    }

    double bpm = m_pEngineBpm->get();
    if (bpm > 0) {
        pGroupFeatures->has_bpm = true;
        pGroupFeatures->bpm = bpm;
    }

    // Without a beatgrid we don't know any beat details.
    if (!m_pBeats) {
        return;
    }

    // Get the current position of this deck.
    double dThisPosition = getCurrentSample();
    double dThisPrevBeat = m_pPrevBeat->get();
    double dThisNextBeat = m_pNextBeat->get();
    double dThisBeatLength;
    double dThisBeatFraction;
    if (getBeatContextNoLookup(dThisPosition,
                       dThisPrevBeat, dThisNextBeat,
                       &dThisBeatLength, &dThisBeatFraction)) {
        pGroupFeatures->has_prev_beat = true;
        pGroupFeatures->prev_beat = dThisPrevBeat;

        pGroupFeatures->has_next_beat = true;
        pGroupFeatures->next_beat = dThisNextBeat;

        pGroupFeatures->has_beat_length = true;
        pGroupFeatures->beat_length = dThisBeatLength;

        pGroupFeatures->has_beat_fraction = true;
        pGroupFeatures->beat_fraction = dThisBeatFraction;
    }
}
Ejemplo n.º 21
0
void BpmControl::slotFileBpmChanged(double bpm) {
    Q_UNUSED(bpm);
    // Adjust the file-bpm with the current setting of the rate to get the
    // engine BPM. We only do this for SYNC_NONE decks because EngineSync will
    // set our BPM if the file BPM changes. See SyncControl::fileBpmChanged().
    if (m_pBeats) {
        const double beats_bpm =
                m_pBeats->getBpmAroundPosition(getCurrentSample(),
                                               kLocalBpmSpan);
        if (beats_bpm != -1) {
            m_pLocalBpm->set(beats_bpm);
        } else {
            m_pLocalBpm->set(bpm);
        }
    } else {
        m_pLocalBpm->set(bpm);
    }
    if (getSyncMode() == SYNC_NONE) {
        slotUpdateEngineBpm();
    }
    resetSyncAdjustment();
}
Ejemplo n.º 22
0
void CueControl::hotcueSet(HotcueControl* pControl, double v) {
    //qDebug() << "CueControl::hotcueSet" << v;

    if (!v)
        return;

    QMutexLocker lock(&m_mutex);
    if (!m_pLoadedTrack)
        return;

    int hotcue = pControl->getHotcueNumber();
    // Note: the cue is just detached from the hotcue control
    // It remains in the database for later use
    // TODO: find a rule, that allows us to delete the cue as well
    // https://bugs.launchpad.net/mixxx/+bug/1653276
    hotcueClear(pControl, v);

    CuePointer pCue(m_pLoadedTrack->createAndAddCue());
    double closestBeat = m_pClosestBeat->get();
    double cuePosition =
            (m_pQuantizeEnabled->toBool() && closestBeat != -1) ?
                    closestBeat : getCurrentSample();
    pCue->setPosition(cuePosition);
    pCue->setHotCue(hotcue);
    pCue->setLabel("");
    pCue->setType(Cue::CUE);
    // TODO(XXX) deal with spurious signals
    attachCue(pCue, hotcue);

    // If quantize is enabled and we are not playing, jump to the cue point
    // since it's not necessarily where we currently are. TODO(XXX) is this
    // potentially invalid for vinyl control?
    bool playing = m_pPlay->toBool();
    if (!playing && m_pQuantizeEnabled->get() > 0.0) {
        lock.unlock();  // prevent deadlock.
        // Enginebuffer will quantize more exactly than we can.
        seekAbs(cuePosition);
    }
}
Ejemplo n.º 23
0
void LoopingControl::slotLoopMove(double beats) {
    if (!m_pTrack || !m_pBeats) {
        return;
    }
    if (m_iLoopStartSample == kNoTrigger || m_iLoopEndSample == kNoTrigger) {
        return;
    }

    double dPosition = getCurrentSample();
    double dBeatLength;
    if (BpmControl::getBeatContext(m_pBeats, dPosition,
                                   NULL, NULL, &dBeatLength, NULL)) {
        int old_loop_in = m_iLoopStartSample;
        int old_loop_out = m_iLoopEndSample;
        int new_loop_in = old_loop_in + (beats * dBeatLength);
        int new_loop_out = old_loop_out + (beats * dBeatLength);
        if (!even(new_loop_in)) {
            --new_loop_in;
        }
        if (!even(new_loop_out)) {
            --new_loop_out;
        }

        m_iLoopStartSample = new_loop_in;
        m_pCOLoopStartPosition->set(new_loop_in);
        m_iLoopEndSample = new_loop_out;
        m_pCOLoopEndPosition->set(new_loop_out);

        // If we are looping make sure that the play head does not leave the
        // loop as a result of our adjustment.
        if (m_bLoopingEnabled) {
            seekInsideAdjustedLoop(old_loop_in, old_loop_out,
                                   new_loop_in, new_loop_out);
        }
    }
}
Ejemplo n.º 24
0
double RateControl::calculateSpeed(double baserate, double speed, bool paused,
                                   int iSamplesPerBuffer,
                                   bool* pReportScratching,
                                   bool* pReportReverse) {
    *pReportScratching = false;
    *pReportReverse = false;
    double rate = (paused ? 0 : 1.0);
    double searching = m_pRateSearch->get();
    if (searching) {
        // If searching is in progress, it overrides everything else
        rate = searching;
    } else {
        double wheelFactor = getWheelFactor();
        double jogFactor = getJogFactor();
        bool bVinylControlEnabled = m_pVCEnabled && m_pVCEnabled->toBool();
        bool useScratch2Value = m_pScratch2Enable->get() != 0;

        // By default scratch2_enable is enough to determine if the user is
        // scratching or not. Moving platter controllers have to disable
        // "scratch2_indicates_scratching" if they are not scratching,
        // to allow things like key-lock.
        if (useScratch2Value && m_pScratch2Scratching->get()) {
            *pReportScratching = true;
        }

        if (bVinylControlEnabled) {
            if (m_pVCScratching->toBool()) {
                *pReportScratching = true;
            }
            rate = speed;
        } else {
            double scratchFactor = m_pScratch2->get();
            // Don't trust values from m_pScratch2
            if (isnan(scratchFactor)) {
                scratchFactor = 0.0;
            }
            if (paused) {
                // Stopped. Wheel, jog and scratch controller all scrub through audio.
                if (useScratch2Value) {
                    rate = scratchFactor + jogFactor + wheelFactor * kWheelMultiplier;
                } else {
                    rate = jogFactor * kPausedJogMultiplier + wheelFactor;
                }
            } else {
                // The buffer is playing, so calculate the buffer rate.

                // There are four rate effects we apply: wheel, scratch, jog and temp.
                // Wheel: a linear additive effect (no spring-back)
                // Scratch: a rate multiplier
                // Jog: a linear additive effect whose value is filtered (springs back)
                // Temp: pitch bend

                // New scratch behavior - overrides playback speed (and old behavior)
                if (useScratch2Value) {
                    rate = scratchFactor;
                } else {
                    rate = speed + getTempRate();
                    rate += wheelFactor;
                }
                rate += jogFactor;
            }
        }

        double currentSample = getCurrentSample();
        m_pScratchController->process(currentSample, rate, iSamplesPerBuffer, baserate);

        // If waveform scratch is enabled, override all other controls
        if (m_pScratchController->isEnabled()) {
            rate = m_pScratchController->getRate();
            *pReportScratching = true;
        } else {
            // If master sync is on, respond to it -- but vinyl and scratch mode always override.
            if (getSyncMode() == SYNC_FOLLOWER && !paused &&
                    !bVinylControlEnabled && !useScratch2Value) {
                if (m_pBpmControl == NULL) {
                    qDebug() << "ERROR: calculateRate m_pBpmControl is null during master sync";
                    return 1.0;
                }

                double userTweak = 0.0;
                if (!*pReportScratching) {
                    // Only report user tweak if the user is not scratching.
                    userTweak = getTempRate() + wheelFactor + jogFactor;
                }
                rate = m_pBpmControl->calcSyncedRate(userTweak);
            }
            // If we are reversing (and not scratching,) flip the rate.  This is ok even when syncing.
            // Reverse with vinyl is only ok if absolute mode isn't on.
            int vcmode = m_pVCMode ? m_pVCMode->get() : MIXXX_VCMODE_ABSOLUTE;
            // TODO(owen): Instead of just ignoring reverse mode, should we
            // disable absolute mode instead?
            if (m_pReverseButton->get()
                    && !m_pScratch2Enable->get()
                    && (!bVinylControlEnabled || vcmode != MIXXX_VCMODE_ABSOLUTE)) {
                rate = -rate;
                *pReportReverse = true;
            }
        }
    }
    return rate;
}
Ejemplo n.º 25
0
bool BpmControl::syncPhase(EngineBuffer* pOtherEngineBuffer) {
    if (!pOtherEngineBuffer) {
        return false;
    }
    TrackPointer otherTrack = pOtherEngineBuffer->getLoadedTrack();
    BeatsPointer otherBeats = otherTrack ? otherTrack->getBeats() : BeatsPointer();

    // If either track does not have beats, then we can't adjust the phase.
    if (!m_pBeats || !otherBeats) {
        return false;
    }

    // Get the file BPM of each song.
    //double dThisBpm = m_pBeats->getBpm();
    //double dOtherBpm = ControlObject::getControl(
    //ConfigKey(pOtherEngineBuffer->getGroup(), "file_bpm"))->get();

    // Get the current position of both decks
    double dThisPosition = getCurrentSample();
    double dOtherLength = ControlObject::getControl(
        ConfigKey(pOtherEngineBuffer->getGroup(), "track_samples"))->get();
    double dOtherEnginePlayPos = ControlObject::getControl(
        ConfigKey(pOtherEngineBuffer->getGroup(), "visual_playposition"))->get();
    double dOtherPosition = dOtherLength * dOtherEnginePlayPos;

    double dThisPrevBeat = m_pBeats->findPrevBeat(dThisPosition);
    double dThisNextBeat = m_pBeats->findNextBeat(dThisPosition);

    if (dThisPrevBeat == -1 || dThisNextBeat == -1 ||
            dOtherEnginePlayPos == -1 || dOtherLength == 0) {
        return false;
    }

    // Protect against the case where we are sitting exactly on the beat.
    if (dThisPrevBeat == dThisNextBeat) {
        dThisNextBeat = m_pBeats->findNthBeat(dThisPosition, 2);
    }

    double dOtherPrevBeat = otherBeats->findPrevBeat(dOtherPosition);
    double dOtherNextBeat = otherBeats->findNextBeat(dOtherPosition);

    if (dOtherPrevBeat == -1 || dOtherNextBeat == -1) {
        return false;
    }

    // Protect against the case where we are sitting exactly on the beat.
    if (dOtherPrevBeat == dOtherNextBeat) {
        dOtherNextBeat = otherBeats->findNthBeat(dOtherPosition, 2);
    }

    double dThisBeatLength = fabs(dThisNextBeat - dThisPrevBeat);
    double dOtherBeatLength = fabs(dOtherNextBeat - dOtherPrevBeat);
    double dOtherBeatFraction = (dOtherPosition - dOtherPrevBeat) / dOtherBeatLength;

    double dNewPlaypos;
    bool this_near_next = dThisNextBeat - dThisPosition <= dThisPosition - dThisPrevBeat;
    bool other_near_next = dOtherNextBeat - dOtherPosition <= dOtherPosition - dOtherPrevBeat;

    // We want our beat fraction to be identical to theirs.

    // If the two tracks have similar alignment, adjust phase is straight-
    // forward.  Use the same fraction for both beats, starting from the previous
    // beat.  But if This track is nearer to the next beat and the Other track
    // is nearer to the previous beat, use This Next beat as the starting point
    // for the phase. (ie, we pushed the sync button late).  If This track
    // is nearer to the previous beat, but the Other track is nearer to the
    // next beat, we pushed the sync button early so use the double-previous
    // beat as the basis for the adjustment.
    //
    // This makes way more sense when you're actually mixing.
    //
    // TODO(XXX) Revisit this logic once we move away from tempo-locked,
    // infinite beatgrids because the assumption that findNthBeat(-2) always
    // works will be wrong then.

    if (this_near_next == other_near_next) {
        dNewPlaypos = dThisPrevBeat + dOtherBeatFraction * dThisBeatLength;
    } else if (this_near_next && !other_near_next) {
        dNewPlaypos = dThisNextBeat + dOtherBeatFraction * dThisBeatLength;
    } else {  //!this_near_next && other_near_next
        dThisPrevBeat = m_pBeats->findNthBeat(dThisPosition, -2);
        dNewPlaypos = dThisPrevBeat + dOtherBeatFraction * dThisBeatLength;
    }

    // We might be seeking outside the loop.
    const bool loop_enabled = m_pLoopEnabled->get() > 0.0;
    const double loop_start_position = m_pLoopStartPosition->get();
    const double loop_end_position = m_pLoopEndPosition->get();

    // Cases for sanity:
    //
    // CASE 1
    // Two identical 1-beat loops, out of phase by X samples.
    // Other deck is at its loop start.
    // This deck is half way through. We want to jump forward X samples to the loop end point.
    //
    // Two identical 1-beat loop, out of phase by X samples.
    // Other deck is

    // If sync target is 50% through the beat,
    // If we are at the loop end point and hit sync, jump forward X samples.


    // TODO(rryan): Revise this with something that keeps a broader number of
    // cases in sync. This at least prevents breaking out of the loop.
    if (loop_enabled) {
        const double loop_length = loop_end_position - loop_start_position;
        if (loop_length <= 0.0) {
            return false;
        }

        // TODO(rryan): If loop_length is not a multiple of dThisBeatLength should
        // we bail and not sync phase?

        // Syncing to after the loop end.
        double end_delta = dNewPlaypos - loop_end_position;
        if (end_delta > 0) {
            int i = end_delta / loop_length;
            dNewPlaypos = loop_start_position + end_delta - i * loop_length;
        }

        // Syncing to before the loop beginning.
        double start_delta = loop_start_position - dNewPlaypos;
        if (start_delta > 0) {
            int i = start_delta / loop_length;
            dNewPlaypos = loop_end_position - start_delta + i * loop_length;
        }
    }

    seekAbs(dNewPlaypos);
    return true;
}
Ejemplo n.º 26
0
void LoopingControl::slotBeatLoop(double beats, bool keepStartPoint) {
    int samples = m_pTrackSamples->get();
    if (!m_pTrack || samples == 0) {
        clearActiveBeatLoop();
        return;
    }


    if (!m_pBeats) {
        clearActiveBeatLoop();
        return;
    }

    // For now we do not handle negative beatloops.
    if (beats < 0) {
        clearActiveBeatLoop();
        return;
    }

    // O(n) search, but there are only ~10-ish beatloop controls so this is
    // fine.
    foreach (BeatLoopingControl* pBeatLoopControl, m_beatLoops) {
        if (pBeatLoopControl->getSize() == beats) {
            if (m_pActiveBeatLoop != pBeatLoopControl) {
                if (m_pActiveBeatLoop) {
                    m_pActiveBeatLoop->deactivate();
                }
                m_pActiveBeatLoop = pBeatLoopControl;
            }
            pBeatLoopControl->activate();
            break;
        }
    }

    // give loop_in and loop_out defaults so we can detect problems
    int loop_in = -1;
    int loop_out = -1;

    // For positive numbers we start from the current position/closest beat and
    // create the loop around X beats from there.
    if (beats > 0) {
        if (keepStartPoint) {
            loop_in = m_iLoopStartSample;
        } 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.
            double cur_pos = getCurrentSample();
            double prevBeat =
                    floorf(m_pBeats->findPrevBeat(cur_pos));

            if (m_pQuantizeEnabled->get() > 0.0 && prevBeat != -1) {
                if (beats >= 1.0) {
                    loop_in = prevBeat;
                } else {
                    // In case of beat length less then 1 beat:
                    // (| - beats, ^ - current track's position):
                    //
                    // ...|...................^........|...
                    //
                    // 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 nextBeat =
                            floorf(m_pBeats->findNextBeat(cur_pos));
                    double beat_len = nextBeat - prevBeat;
                    double loops_per_beat = 1.0 / beats;
                    double beat_pos = cur_pos - prevBeat;
                    int beat_frac =
                            static_cast<int>(floor((beat_pos / beat_len) *
                                                   loops_per_beat));
                    loop_in = prevBeat + beat_len / loops_per_beat * beat_frac;
                }

            } else {
                loop_in = floorf(cur_pos);
            }


            if (!even(loop_in)) {
                loop_in--;
            }
        }

        int fullbeats = static_cast<int>(floorf(beats));
        double fracbeats = beats - static_cast<double>(fullbeats);

        // Now we need to calculate the length of the beatloop. We do this by
        // taking the current beat and the fullbeats'th beat and measuring the
        // distance between them.
        loop_out = loop_in;

        if (fullbeats > 0) {
            // Add the length between this beat and the fullbeats'th beat to the
            // loop_out position;
            double this_beat = m_pBeats->findNthBeat(loop_in, 1);
            double nth_beat = m_pBeats->findNthBeat(loop_in, 1 + fullbeats);
            loop_out += (nth_beat - this_beat);
        }

        if (fracbeats > 0) {
            // Add the fraction of the beat following the current loop_out
            // position to loop out.
            double loop_out_beat = m_pBeats->findNthBeat(loop_out, 1);
            double loop_out_next_beat = m_pBeats->findNthBeat(loop_out, 2);
            loop_out += (loop_out_next_beat - loop_out_beat) * fracbeats;
        }
    }

    if ((loop_in == -1) || ( loop_out == -1))
        return;

    if (!even(loop_in))
        loop_in--;
    if (!even(loop_out))
        loop_out--;

    if (loop_in == loop_out) {
        if ((loop_out+2) > samples) {
            loop_in -= 2;
        } else {
            loop_out += 2;
        }
    } else if (loop_out > samples) {
        // Do not allow beat loops to go beyond the end of the track
        loop_out = samples;
    }

    m_iLoopStartSample = loop_in;
    m_pCOLoopStartPosition->set(loop_in);
    m_iLoopEndSample = loop_out;
    m_pCOLoopEndPosition->set(loop_out);
    setLoopingEnabled(true);
}
Ejemplo n.º 27
0
double RateControl::calculateRate(double baserate, bool paused,
                                  int iSamplesPerBuffer, bool* isScratching) {
    double rate = (paused ? 0 : 1.0);

    double searching = m_pRateSearch->get();
    if (searching) {
        // If searching is in progress, it overrides everything else
        rate = searching;
    } else {
        double wheelFactor = getWheelFactor();
        double jogFactor = getJogFactor();
        bool bVinylControlEnabled = m_pVCEnabled && m_pVCEnabled->get() > 0.0;
        bool scratchEnable = m_pScratchToggle->get() != 0 || bVinylControlEnabled;

        double scratchFactor = m_pScratch->get();
        // Don't trust values from m_pScratch
        if (isnan(scratchFactor)) {
            scratchFactor = 0.0;
        }

        // Old Scratch works without scratchEnable
        double oldScratchFactor = m_pOldScratch->get(); // Deprecated
        // Don't trust values from m_pScratch
        if (isnan(oldScratchFactor)) {
            oldScratchFactor = 0.0;
        }

        // If vinyl control is enabled and scratching then also set isScratching
        bool bVinylControlScratching = m_pVCScratching && m_pVCScratching->get() > 0.0;
        if (bVinylControlEnabled && bVinylControlScratching) {
            *isScratching = true;
        }

        if (paused) {
            // Stopped. Wheel, jog and scratch controller all scrub through audio.
            // New scratch behavior overrides old
            if (scratchEnable) {
                rate = scratchFactor + jogFactor + wheelFactor * 40.0;
            } else {
                // Just remove oldScratchFactor in future
                rate = oldScratchFactor + jogFactor * 18 + wheelFactor;
            }
        } else {
            // The buffer is playing, so calculate the buffer rate.

            // There are four rate effects we apply: wheel, scratch, jog and temp.
            // Wheel: a linear additive effect (no spring-back)
            // Scratch: a rate multiplier
            // Jog: a linear additive effect whose value is filtered (springs back)
            // Temp: pitch bend

            // New scratch behavior - overrides playback speed (and old behavior)
            if (scratchEnable) {
                rate = scratchFactor;
            } else {

                rate = 1. + getRawRate() + getTempRate();
                rate += wheelFactor;

                // Deprecated old scratch behavior
                if (oldScratchFactor < 0.) {
                    rate *= (oldScratchFactor - 1.);
                } else if (oldScratchFactor > 0.) {
                    rate *= (oldScratchFactor + 1.);
                }
            }

            rate += jogFactor;

        }

        double currentSample = getCurrentSample();
        m_pScratchController->process(currentSample, rate, iSamplesPerBuffer, baserate);

        // If waveform scratch is enabled, override all other controls
        if (m_pScratchController->isEnabled()) {
            rate = m_pScratchController->getRate();
            *isScratching = true;
        }

        // If master sync is on, respond to it -- but vinyl and scratch mode always override.
        if (getSyncMode() == SYNC_FOLLOWER && !paused &&
            !bVinylControlEnabled && !*isScratching) {
            if (m_pBpmControl == NULL) {
                qDebug() << "ERROR: calculateRate m_pBpmControl is null during master sync";
                return 1.0;
            }

            rate = m_pBpmControl->getSyncedRate();
            double userTweak = getTempRate() + wheelFactor + jogFactor;
            bool userTweakingSync = userTweak != 0.0;
            rate += userTweak;

            rate *= m_pBpmControl->getSyncAdjustment(userTweakingSync);
        }
        // If we are reversing (and not scratching,) flip the rate.  This is ok even when syncing.
        if (!scratchEnable && m_pReverseButton->get()) {
            rate = -rate;
        }
    }

    return rate;
}
Ejemplo n.º 28
0
bool CueControl::isTrackAtCue() {
    return (fabs(getCurrentSample() - m_pCuePoint->get()) < 1.0f);
}
Ejemplo n.º 29
0
soundDataBuffer* sound_DecodeOggVorbis(struct OggVorbisDecoderState* decoder, size_t bufferSize)
{
	size_t		size = 0;
#ifndef WZ_NOSOUND
	int		result;
#endif

	soundDataBuffer* buffer;

	ASSERT(decoder != NULL, "NULL decoder passed!");

#ifndef WZ_NOSOUND
	if (decoder->allowSeeking)
	{
		unsigned int sampleCount = getSampleCount(decoder);

		unsigned int sizeEstimate = sampleCount * decoder->VorbisInfo->channels * 2;

		if (((bufferSize == 0) || (bufferSize > sizeEstimate)) && (sizeEstimate != 0))
		{
			bufferSize = (sampleCount - getCurrentSample(decoder)) * decoder->VorbisInfo->channels * 2;
		}
	}

	// If we can't seek nor receive any suggested size for our buffer, just quit
	if (bufferSize == 0)
	{
		debug(LOG_ERROR, "can't find a proper buffer size");
		return NULL;
	}
#else
	bufferSize = 0;
#endif

	buffer = malloc(bufferSize + sizeof(soundDataBuffer));
	if (buffer == NULL)
	{
		debug(LOG_ERROR, "couldn't allocate memory (%lu bytes requested)", (unsigned long) bufferSize + sizeof(soundDataBuffer));
		return NULL;
	}

	buffer->data = (char*)(buffer + 1);
	buffer->bufferSize = bufferSize;
	buffer->bitsPerSample = 16;

#ifndef WZ_NOSOUND
	buffer->channelCount = decoder->VorbisInfo->channels;
	buffer->frequency = decoder->VorbisInfo->rate;

	// Decode PCM data into the buffer until there is nothing to decode left
	do
	{
		// Decode
		int section;
		result = ov_read(&decoder->oggVorbis_stream, &buffer->data[size], bufferSize - size, OGG_ENDIAN, 2, 1, &section);

		if (result < 0)
		{
			debug(LOG_ERROR, "error decoding from OggVorbis file; errorcode from ov_read: %d", result);
			free(buffer);
			return NULL;
		}
		else
		{
			size += result;
		}

	} while ((result != 0 && size < bufferSize));
#endif

	buffer->size = size;

	return buffer;
}
Ejemplo n.º 30
0
double CueControl::updateIndicatorsAndModifyPlay(double play, bool playPossible) {
    QMutexLocker lock(&m_mutex);
    double cueMode = m_pCueMode->get();

    if ((cueMode == CUE_MODE_DENON || cueMode == CUE_MODE_NUMARK) &&
            play > 0.0 &&
            playPossible &&
            m_pPlayButton->get() == 0.0) {
        // in Denon mode each play from pause moves the cue point
        // if not previewing
        cueSet(1.0);
    }

    // when previewing, "play" was set by cue button, a following toggle request
    // (play = 0.0) is used for latching play.
    bool previewing = false;
    if (m_bPreviewing || m_bPreviewingHotcue) {
        if (play == 0.0) {
            // play latch request: stop previewing and go into normal play mode.
            m_bPreviewing = false;
            m_bHotcueCancel = true;
            play = 1.0;
        } else {
            previewing = true;
        }
    }

    if (!playPossible) {
        // play not possible
        play = 0.0;
        m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF);
        m_pStopButton->set(0.0);
    } else if (play && !previewing) {
        // Play: Indicates a latched Play
        m_pPlayIndicator->setBlinkValue(ControlIndicator::ON);
        m_pStopButton->set(0.0);
    } else {
        // Pause:
        m_pStopButton->set(1.0);
        if (cueMode == CUE_MODE_DENON) {
            if (isTrackAtCue()) {
                m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF);
            } else {
                // Flashing indicates that a following play would move cue point
                m_pPlayIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_500MS);
            }
        } else if (cueMode == CUE_MODE_MIXXX || cueMode == CUE_MODE_NUMARK) {
            m_pPlayIndicator->setBlinkValue(ControlIndicator::OFF);
        } else {
            // Flashing indicates that play is possible in Pioneer mode
            m_pPlayIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_500MS);
        }
    }

    if (cueMode != CUE_MODE_DENON && cueMode != CUE_MODE_NUMARK) {
        if (m_pCuePoint->get() != -1) {
            if (play == 0.0 && !isTrackAtCue() &&
                    getCurrentSample() < getTotalSamples()) {
                if (cueMode == CUE_MODE_MIXXX) {
                    // in Mixxx mode Cue Button is flashing slow if CUE will move Cue point
                    m_pCueIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_500MS);
                } else {
                    // in Pioneer mode Cue Button is flashing fast if CUE will move Cue point
                    m_pCueIndicator->setBlinkValue(ControlIndicator::RATIO1TO1_250MS);
                }
            } else {
                m_pCueIndicator->setBlinkValue(ControlIndicator::OFF);
            }
        } else {
            m_pCueIndicator->setBlinkValue(ControlIndicator::OFF);
        }
    }
    m_pPlayStutter->set(play);

    return play;
}