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); } }
void LoopingControl::slotLoopHalve(double v) { if (v > 0.0) { // If a beatloop is active then halve should deactive the current // beatloop and activate the previous one. if (m_pActiveBeatLoop != NULL) { int active_index = m_beatLoops.indexOf(m_pActiveBeatLoop); if (active_index - 1 >= 0) { if (m_bLoopingEnabled) { // If the current position is outside the range of the new loop, // take the current position and subtract the length of the new loop until // it fits. int old_loop_in = m_iLoopStartSample; int old_loop_out = m_iLoopEndSample; slotBeatLoopActivate(m_beatLoops[active_index - 1]); seekInsideAdjustedLoop( old_loop_in, old_loop_out, m_iLoopStartSample, m_iLoopEndSample); } else { // Calling scale clears the active beatloop. slotLoopScale(0.5); m_pActiveBeatLoop = m_beatLoops[active_index - 1]; } } } else { slotLoopScale(0.5); } } }
void LoopingControl::slotLoopScale(double scale) { if (m_iLoopStartSample == kNoTrigger || m_iLoopEndSample == kNoTrigger) { return; } int loop_length = m_iLoopEndSample - m_iLoopStartSample; int old_loop_end = m_iLoopEndSample; int samples = m_pTrackSamples->get(); loop_length *= scale; // Abandon loops that are too short of extend beyond the end of the file. if (loop_length < MINIMUM_AUDIBLE_LOOP_SIZE || m_iLoopStartSample + loop_length > samples) { return; } m_iLoopEndSample = m_iLoopStartSample + loop_length; if (!even(m_iLoopEndSample)) { m_iLoopEndSample--; } // TODO(XXX) we could be smarter about taking the active beatloop, scaling // it by the desired amount and trying to find another beatloop that matches // it, but for now we just clear the active beat loop if somebody scales. clearActiveBeatLoop(); // Don't allow 0 samples loop, so one can still manipulate it if (m_iLoopEndSample == m_iLoopStartSample) { if ((m_iLoopEndSample+2) >= samples) m_iLoopStartSample -= 2; else m_iLoopEndSample += 2; } // Do not allow loops to go past the end of the song else if (m_iLoopEndSample > samples) { m_iLoopEndSample = samples; } // Update CO for loop end marker m_pCOLoopEndPosition->set(m_iLoopEndSample); // Reseek if the loop shrank out from under the playposition. if (m_bLoopingEnabled && scale < 1.0) { seekInsideAdjustedLoop( m_iLoopStartSample, old_loop_end, m_iLoopStartSample, m_iLoopEndSample); } }
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); } } }
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; double nextBeat; m_pBeats->findPrevNextBeats(cur_pos, &prevBeat, &nextBeat); 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 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 = floor(cur_pos); } if (!even(loop_in)) { loop_in--; } } int fullbeats = static_cast<int>(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; // TODO: figure out how to convert this to a findPrevNext call. 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. // TODO: figure out how to convert this to a findPrevNext call. 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; } if (keepStartPoint) { seekInsideAdjustedLoop(m_iLoopStartSample, m_iLoopEndSample, loop_in, loop_out); } m_iLoopStartSample = loop_in; m_pCOLoopStartPosition->set(loop_in); m_iLoopEndSample = loop_out; m_pCOLoopEndPosition->set(loop_out); setLoopingEnabled(true); }