Beispiel #1
0
BeatsPointer BeatFactory::makePreferredBeats(
    TrackPointer pTrack, QVector<double> beats,
    const QHash<QString, QString> extraVersionInfo,
    const bool bEnableFixedTempoCorrection, const bool bEnableOffsetCorrection,
    const int iSampleRate, const int iTotalSamples,
    const int iMinBpm, const int iMaxBpm) {
    const QString version = getPreferredVersion(bEnableFixedTempoCorrection);
    const QString subVersion = getPreferredSubVersion(bEnableFixedTempoCorrection,
                               bEnableOffsetCorrection,
                               iMinBpm, iMaxBpm,
                               extraVersionInfo);

    BeatUtils::printBeatStatistics(beats, iSampleRate);
    if (version == BEAT_GRID_2_VERSION) {
        double globalBpm = BeatUtils::calculateBpm(beats, iSampleRate, iMinBpm, iMaxBpm);
        double firstBeat = BeatUtils::calculateFixedTempoFirstBeat(
                               bEnableOffsetCorrection,
                               beats, iSampleRate, iTotalSamples, globalBpm);
        BeatGrid* pGrid = new BeatGrid(pTrack.data(), iSampleRate);
        // firstBeat is in frames here and setGrid() takes samples.
        pGrid->setGrid(globalBpm, firstBeat * 2);
        pGrid->setSubVersion(subVersion);
        return BeatsPointer(pGrid, &BeatFactory::deleteBeats);
    } else if (version == BEAT_MAP_VERSION) {
        BeatMap* pBeatMap = new BeatMap(pTrack, iSampleRate, beats);
        pBeatMap->setSubVersion(subVersion);
        return BeatsPointer(pBeatMap, &BeatFactory::deleteBeats);
    } else {
        qDebug() << "ERROR: Could not determine what type of beatgrid to create.";
        return BeatsPointer();
    }
}
Beispiel #2
0
void TrackInfoObject::setBpm(double f) {
    if (f < 0) {
        return;
    }

    QMutexLocker lock(&m_qMutex);
    // TODO(rryan): Assume always dirties.
    bool dirty = false;
    if (f == 0.0) {
        // If the user sets the BPM to 0, we assume they want to clear the
        // beatgrid.
        setBeats(BeatsPointer());
        dirty = true;
    } else if (!m_pBeats) {
        setBeats(BeatFactory::makeBeatGrid(this, f, 0));
        dirty = true;
    } else if (m_pBeats->getBpm() != f) {
        m_pBeats->setBpm(f);
        dirty = true;
    }

    if (dirty) {
        setDirty(true);
    }

    lock.unlock();
    // Tell the GUI to update the bpm label...
    //qDebug() << "TrackInfoObject signaling BPM update to" << f;
    emit(bpmUpdated(f));
}
Beispiel #3
0
BeatsPointer BeatFactory::loadBeatsFromByteArray(TrackPointer pTrack,
        QString beatsVersion,
        QString beatsSubVersion,
        QByteArray* beatsSerialized) {
    if (beatsVersion == BEAT_GRID_1_VERSION ||
            beatsVersion == BEAT_GRID_2_VERSION) {
        BeatGrid* pGrid = new BeatGrid(pTrack.data(), 0, beatsSerialized);
        pGrid->setSubVersion(beatsSubVersion);
        qDebug() << "Successfully deserialized BeatGrid";
        return BeatsPointer(pGrid, &BeatFactory::deleteBeats);
    } else if (beatsVersion == BEAT_MAP_VERSION) {
        BeatMap* pMap = new BeatMap(pTrack, 0, beatsSerialized);
        pMap->setSubVersion(beatsSubVersion);
        qDebug() << "Successfully deserialized BeatMap";
        return BeatsPointer(pMap, &BeatFactory::deleteBeats);
    }
    qDebug() << "BeatFactory::loadBeatsFromByteArray could not parse serialized beats.";
    return BeatsPointer();
}
Beispiel #4
0
double BpmControl::getNearestPositionInPhase(double dThisPosition, bool respectLoops, bool playing) {
    // Without a beatgrid, we don't know the phase offset.
    if (!m_pBeats) {
        return dThisPosition;
    }
    // Master buffer is always in sync!
    if (getSyncMode() == SYNC_MASTER) {
        return dThisPosition;
    }

    // Get the current position of this deck.
    double dThisPrevBeat = m_pPrevBeat->get();
    double dThisNextBeat = m_pNextBeat->get();
    double dThisBeatLength;
    if (dThisPosition > dThisNextBeat || dThisPosition < dThisPrevBeat) {
        // There's a chance the COs might be out of date, so do a lookup.
        // TODO: figure out a way so that quantized control can take care of
        // this so this call isn't necessary.
        if (!getBeatContext(m_pBeats, dThisPosition,
                            &dThisPrevBeat, &dThisNextBeat,
                            &dThisBeatLength, NULL)) {
            return dThisPosition;
        }
    } else {
        if (!getBeatContextNoLookup(dThisPosition,
                                    dThisPrevBeat, dThisNextBeat,
                                    &dThisBeatLength, NULL)) {
            return dThisPosition;
        }
    }

    double dOtherBeatFraction;
    if (getSyncMode() == SYNC_FOLLOWER) {
        // If we're a follower, it's easy to get the other beat fraction
        dOtherBeatFraction = m_dSyncTargetBeatDistance;
    } else {
        // If not, we have to figure it out
        EngineBuffer* pOtherEngineBuffer = pickSyncTarget();
        if (pOtherEngineBuffer == NULL) {
            return dThisPosition;
        }

        if (playing) {
            // "this" track is playing, or just starting
            // only match phase if the sync target is playing as well
            if (pOtherEngineBuffer->getSpeed() == 0.0) {
                return dThisPosition;
            }
        }

        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 (!otherBeats) {
            return dThisPosition;
        }

        double dOtherLength = ControlObject::getControl(
                ConfigKey(pOtherEngineBuffer->getGroup(), "track_samples"))->get();
        double dOtherEnginePlayPos = pOtherEngineBuffer->getVisualPlayPos();
        double dOtherPosition = dOtherLength * dOtherEnginePlayPos;

        if (!BpmControl::getBeatContext(otherBeats, dOtherPosition,
                                        NULL, NULL, NULL, &dOtherBeatFraction)) {
            return dThisPosition;
        }
    }

    bool this_near_next = dThisNextBeat - dThisPosition <= dThisPosition - dThisPrevBeat;
    bool other_near_next = dOtherBeatFraction >= 0.5;

    // 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.

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

    if (respectLoops) {
        // We might be seeking outside the loop.
        const bool loop_enabled = m_pLoopEnabled->toBool();
        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 &&
                dThisPosition <= loop_end_position) {
            const double loop_length = loop_end_position - loop_start_position;
            const double end_delta = dNewPlaypos - loop_end_position;

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

                // Move new position after loop jump into phase as well.
                // This is a recursive call, called only twice because of
                // respectLoops = false
                dNewPlaypos = getNearestPositionInPhase(dNewPlaypos, false, playing);
            }

            // Note: Syncing to before the loop beginning is allowed, because
            // loops are catching
        }
    }

    return dNewPlaypos;
}
Beispiel #5
0
BeatsPointer BeatFactory::makeBeatGrid(Track* pTrack, double dBpm,
                                       double dFirstBeatSample) {
    BeatGrid* pGrid = new BeatGrid(pTrack, 0);
    pGrid->setGrid(dBpm, dFirstBeatSample);
    return BeatsPointer(pGrid, &BeatFactory::deleteBeats);
}
Beispiel #6
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;
}