=== modified file 'mixxx/res/skins/LateNight1280x800-WXGA/skin.xml'
--- mixxx/res/skins/LateNight1280x800-WXGA/skin.xml 2012-05-31 20:28:19 +0000
+++ mixxx/res/skins/LateNight1280x800-WXGA/skin.xml 2012-06-03 03:25:07 +0000
@@ -4215,6 +4215,7 @@
2
true
+ true
0
btn_beatloop1_0125.png
@@ -4233,6 +4234,11 @@
true
+ [Channel1],beatlooproll_0.125_activate
+ true
+ RightButton
+
+
[Channel1],beatloop_0.125_enabled
false
@@ -4242,6 +4248,7 @@
2
true
+ true
0
btn_beatloop1_0250.png
@@ -4260,6 +4267,11 @@
true
+ [Channel1],beatlooproll_0.25_activate
+ true
+ RightButton
+
+
[Channel1],beatloop_0.25_enabled
false
@@ -4269,6 +4281,7 @@
2
true
+ true
0
btn_beatloop1_0500.png
@@ -4287,6 +4300,11 @@
true
+ [Channel1],beatlooproll_0.5_activate
+ true
+ RightButton
+
+
[Channel1],beatloop_0.5_enabled
false
@@ -4296,6 +4314,7 @@
2
true
+ true
0
btn_beatloop1_1.png
@@ -4314,6 +4333,11 @@
true
+ [Channel1],beatlooproll_1_activate
+ true
+ RightButton
+
+
[Channel1],beatloop_1_enabled
false
@@ -4323,6 +4347,7 @@
2
true
+ true
0
btn_beatloop1_2.png
@@ -4341,6 +4366,11 @@
true
+ [Channel1],beatlooproll_2_activate
+ true
+ RightButton
+
+
[Channel1],beatloop_2_enabled
false
@@ -4350,6 +4380,7 @@
2
true
+ true
0
btn_beatloop1_4.png
@@ -4368,6 +4399,11 @@
true
+ [Channel1],beatlooproll_4_activate
+ true
+ RightButton
+
+
[Channel1],beatloop_4_enabled
false
@@ -4377,6 +4413,7 @@
2
true
+ true
0
btn_beatloop1_8.png
@@ -4395,6 +4432,11 @@
true
+ [Channel1],beatlooproll_8_activate
+ true
+ RightButton
+
+
[Channel1],beatloop_8_enabled
false
@@ -4404,6 +4446,7 @@
2
true
+ true
0
btn_beatloop1_16.png
@@ -4422,6 +4465,11 @@
true
+ [Channel1],beatlooproll_16_activate
+ true
+ RightButton
+
+
[Channel1],beatloop_16_enabled
false
@@ -5164,6 +5212,7 @@
2
true
+ true
0
btn_beatloop2_0125.png
@@ -5182,6 +5231,11 @@
true
+ [Channel2],beatlooproll_0.125_activate
+ true
+ RightButton
+
+
[Channel2],beatloop_0.125_enabled
false
@@ -5191,6 +5245,7 @@
2
true
+ true
0
btn_beatloop2_0250.png
@@ -5209,6 +5264,11 @@
true
+ [Channel2],beatlooproll_0.25_activate
+ true
+ RightButton
+
+
[Channel2],beatloop_0.25_enabled
false
@@ -5218,6 +5278,7 @@
2
true
+ true
0
btn_beatloop2_0500.png
@@ -5236,6 +5297,11 @@
true
+ [Channel2],beatlooproll_0.5_activate
+ true
+ RightButton
+
+
[Channel2],beatloop_0.5_enabled
false
@@ -5245,6 +5311,7 @@
2
true
+ true
0
btn_beatloop2_1.png
@@ -5263,6 +5330,11 @@
true
+ [Channel2],beatlooproll_1_activate
+ true
+ RightButton
+
+
[Channel2],beatloop_1_enabled
false
@@ -5272,6 +5344,7 @@
2
true
+ true
0
btn_beatloop2_2.png
@@ -5290,6 +5363,11 @@
true
+ [Channel2],beatlooproll_2_activate
+ true
+ RightButton
+
+
[Channel2],beatloop_2_enabled
false
@@ -5299,6 +5377,7 @@
2
true
+ true
0
btn_beatloop2_4.png
@@ -5317,6 +5396,11 @@
true
+ [Channel2],beatlooproll_4_activate
+ true
+ RightButton
+
+
[Channel2],beatloop_4_enabled
false
@@ -5326,6 +5410,7 @@
2
true
+ true
0
btn_beatloop2_8.png
@@ -5344,6 +5429,11 @@
true
+ [Channel2],beatlooproll_8_activate
+ true
+ RightButton
+
+
[Channel2],beatloop_8_enabled
false
@@ -5353,6 +5443,7 @@
2
true
+ true
0
btn_beatloop2_16.png
@@ -5371,6 +5462,11 @@
true
+ [Channel2],beatlooproll_16_activate
+ true
+ RightButton
+
+
[Channel2],beatloop_16_enabled
false
=== modified file 'mixxx/src/engine/enginebuffer.cpp'
--- mixxx/src/engine/enginebuffer.cpp 2012-05-05 02:36:53 +0000
+++ mixxx/src/engine/enginebuffer.cpp 2012-06-05 04:39:37 +0000
@@ -82,13 +82,19 @@
m_fRampValue(0.0),
m_iRampState(ENGINE_RAMP_NONE),
m_pDitherBuffer(new CSAMPLE[MAX_BUFFER_LEN]),
- m_iDitherBufferReadIndex(0) {
+ m_iDitherBufferReadIndex(0),
+ m_pCrossFadeBuffer(new CSAMPLE[MAX_BUFFER_LEN]),
+ m_iCrossFadeSamples(0),
+ m_iLastBufferSize(0) {
// Generate dither values
for (int i = 0; i < MAX_BUFFER_LEN; ++i) {
m_pDitherBuffer[i] = static_cast(rand() % 32768) / 32768.0 - 0.5;
}
+ //zero out crossfade buffer
+ SampleUtil::applyGain(m_pCrossFadeBuffer, 0.0, MAX_BUFFER_LEN);
+
m_fLastSampleValue[0] = 0;
m_fLastSampleValue[1] = 0;
@@ -150,6 +156,15 @@
connect(endButton, SIGNAL(valueChanged(double)),
this, SLOT(slotControlEnd(double)),
Qt::DirectConnection);
+
+ m_pSlipButton = new ControlPushButton(ConfigKey(group, "slip_enabled"));
+ m_pSlipButton->setButtonMode(ControlPushButton::TOGGLE);
+ connect(m_pSlipButton, SIGNAL(valueChanged(double)),
+ this, SLOT(slotControlSlip(double)),
+ Qt::DirectConnection);
+ connect(m_pSlipButton, SIGNAL(valueChangedFromEngine(double)),
+ this, SLOT(slotControlSlip(double)),
+ Qt::DirectConnection);
// Actual rate (used in visuals, not for control)
rateEngine = new ControlObject(ConfigKey(group, "rateEngine"));
@@ -178,7 +193,7 @@
m_pRepeat = new ControlPushButton(ConfigKey(group, "repeat"));
m_pRepeat->setButtonMode(ControlPushButton::TOGGLE);
-
+
#ifdef __VINYLCONTROL__
//a midi knob to tweak the vinyl pitch for decks with crappy sliders
m_pVinylPitchTweakKnob = new ControlPotmeter(ConfigKey(_group, "vinylpitchtweak"), -0.005, 0.005);
@@ -344,6 +359,15 @@
void EngineBuffer::setNewPlaypos(double newpos)
{
//qDebug() << "engine new pos " << newpos;
+
+ // Before seeking, read extra buffer for crossfading
+ CSAMPLE *fadeout;
+ fadeout = m_pScale->scale(0,
+ m_iLastBufferSize,
+ 0,
+ 0);
+ m_iCrossFadeSamples = m_iLastBufferSize;
+ SampleUtil::copyWithGain(m_pCrossFadeBuffer, fadeout, 1.0, m_iLastBufferSize);
filepos_play = newpos;
@@ -354,7 +378,7 @@
if (m_pScale)
m_pScale->clear();
m_pReadAheadManager->notifySeek(filepos_play);
-
+
// Must hold the engineLock while using m_engineControls
m_engineLock.lock();
for (QList::iterator it = m_engineControls.begin();
@@ -449,7 +473,7 @@
// Ensure that the file position is even (remember, stereo channel files...)
if (!even((int)new_playpos))
new_playpos--;
-
+
setNewPlaypos(new_playpos);
}
@@ -512,6 +536,27 @@
}
}
+void EngineBuffer::slotControlSlip(double v)
+{
+ bool enabled = v > 0.0;
+ if (enabled == m_bSlipEnabled) {
+ return;
+ }
+
+ m_bSlipEnabled = enabled;
+
+ if (enabled) {
+ m_dSlipPosition = filepos_play;
+ m_dSlipRate = rate_old;
+ }
+ else
+ {
+ //TODO(owen) assuming that looping will get cancelled properly
+ slotControlSeekAbs(m_dSlipPosition);
+ }
+}
+
+
void EngineBuffer::process(const CSAMPLE *, const CSAMPLE * pOut, const int iBufferSize)
{
Q_ASSERT(even(iBufferSize));
@@ -544,6 +589,12 @@
&is_scratching);
//qDebug() << "rate" << rate << " paused" << paused;
+
+ // Update the slipped position
+ if (m_bSlipEnabled) {
+ m_dSlipPosition += static_cast(iBufferSize) * m_dSlipRate;
+ }
+
// Scratching always disables keylock because keylock sounds terrible
// when not going at a constant rate.
@@ -632,6 +683,31 @@
m_pReadAheadManager->getEffectiveVirtualPlaypositionFromLog(
static_cast(filepos_play), samplesRead);
} // else (bCurBufferPaused)
+
+ //Crossfade if we just did a seek
+ if (m_iCrossFadeSamples > 0)
+ {
+ int i = 0;
+ double cross_len = 0;
+ if (m_iCrossFadeSamples >= iBufferSize) {
+ i = m_iCrossFadeSamples - iBufferSize;
+ cross_len = static_cast(iBufferSize) / 2.0;
+ } else {
+ cross_len = static_cast(m_iCrossFadeSamples) / 2.0;
+ }
+
+ double cross_mix = 0.0;
+ double cross_inc = 1.0 / cross_len;
+
+ // Do crossfade from old fadeout buffer to this new data
+ for (int j=0; i < m_iCrossFadeSamples; i+=2, j+=2)
+ {
+ pOutput[j] = pOutput[j] * cross_mix + m_pCrossFadeBuffer[i] * (1.0 - cross_mix);
+ pOutput[j+1] = pOutput[j+1] * cross_mix + m_pCrossFadeBuffer[i+1] * (1.0 - cross_mix);
+ cross_mix += cross_inc;
+ }
+ m_iCrossFadeSamples = 0;
+ }
m_engineLock.lock();
QListIterator it(m_engineControls);
@@ -779,8 +855,9 @@
writer << pOutput[i] << "\n";
}
#endif
-
+
m_bLastBufferPaused = bCurBufferPaused;
+ m_iLastBufferSize = iBufferSize;
}
void EngineBuffer::updateIndicators(double rate, int iBufferSize) {
@@ -826,6 +903,16 @@
m_hintList.clear();
m_pReadAheadManager->hintReader(dRate, m_hintList, iSourceSamples);
+
+ //if slipping, hint about virtual position so we're ready for it
+ if (m_bSlipEnabled)
+ {
+ Hint hint;
+ hint.length = 2048; //default length please
+ hint.sample = m_dSlipRate >= 0 ? m_dSlipPosition : m_dSlipPosition - 2048;
+ hint.priority = 1;
+ m_hintList.append(hint);
+ }
QListIterator it(m_engineControls);
while (it.hasNext()) {
=== modified file 'mixxx/src/engine/enginebuffer.h'
--- mixxx/src/engine/enginebuffer.h 2012-05-10 00:21:25 +0000
+++ mixxx/src/engine/enginebuffer.h 2012-06-05 04:39:37 +0000
@@ -131,6 +131,7 @@
void slotControlEnd(double);
void slotControlSeek(double);
void slotControlSeekAbs(double);
+ void slotControlSlip(double);
// Request that the EngineBuffer load a track. Since the process is
// asynchronous, EngineBuffer will emit a trackLoaded signal when the load
@@ -206,11 +207,19 @@
/** Used in update of playpos slider */
int m_iSamplesCalculated;
int m_iUiSlowTick;
-
+
+ /** The location where the track would have been had slip not been engaged */
+ double m_dSlipPosition;
+ /** Saved value of rate for slip mode */
+ double m_dSlipRate;
+ /** Slip Status */
+ bool m_bSlipEnabled;
+
ControlObject* m_pTrackSamples;
ControlObject* m_pTrackSampleRate;
ControlPushButton *playButton, *buttonBeatSync, *playStartButton, *stopStartButton, *stopButton, *playSyncButton;
+ ControlPushButton *m_pSlipButton;
ControlObjectThreadMain *playButtonCOT, *playStartButtonCOT, *stopStartButtonCOT, *m_pTrackEndCOT, *stopButtonCOT;
ControlObject *fwdButton, *backButton;
@@ -265,6 +274,9 @@
#endif
CSAMPLE* m_pDitherBuffer;
unsigned int m_iDitherBufferReadIndex;
+ CSAMPLE* m_pCrossFadeBuffer;
+ int m_iCrossFadeSamples;
+ int m_iLastBufferSize;
};
#endif
=== modified file 'mixxx/src/engine/loopingcontrol.cpp'
--- mixxx/src/engine/loopingcontrol.cpp 2012-05-05 02:36:01 +0000
+++ mixxx/src/engine/loopingcontrol.cpp 2012-06-02 21:15:29 +0000
@@ -68,6 +68,7 @@
m_pNextBeat = ControlObject::getControl(ConfigKey(_group, "beat_next"));
m_pClosestBeat = ControlObject::getControl(ConfigKey(_group, "beat_closest"));
m_pTrackSamples = ControlObject::getControl(ConfigKey(_group,"track_samples"));
+ m_pSlipEnabled = ControlObject::getControl(ConfigKey(_group,"slip_enabled"));
// Connect beatloop, which can flexibly handle different values.
// Using this CO directly is meant to be used internally and by scripts,
@@ -84,9 +85,15 @@
connect(pBeatLoop, SIGNAL(activateBeatLoop(BeatLoopingControl*)),
this, SLOT(slotBeatLoopActivate(BeatLoopingControl*)),
Qt::DirectConnection);
+ connect(pBeatLoop, SIGNAL(activateBeatLoopRoll(BeatLoopingControl*)),
+ this, SLOT(slotBeatLoopActivateRoll(BeatLoopingControl*)),
+ Qt::DirectConnection);
connect(pBeatLoop, SIGNAL(deactivateBeatLoop(BeatLoopingControl*)),
this, SLOT(slotBeatLoopDeactivate(BeatLoopingControl*)),
Qt::DirectConnection);
+ connect(pBeatLoop, SIGNAL(deactivateBeatLoopRoll(BeatLoopingControl*)),
+ this, SLOT(slotBeatLoopDeactivateRoll(BeatLoopingControl*)),
+ Qt::DirectConnection);
m_beatLoops.append(pBeatLoop);
}
@@ -520,10 +527,25 @@
beatLoopAlreadyActive && m_bLoopingEnabled);
}
+void LoopingControl::slotBeatLoopActivateRoll(BeatLoopingControl* pBeatLoopControl) {
+ if (!m_pTrack) {
+ return;
+ }
+
+ //Disregard existing loops
+ m_pSlipEnabled->set(1);
+ slotBeatLoop(pBeatLoopControl->getSize(), false);
+}
+
void LoopingControl::slotBeatLoopDeactivate(BeatLoopingControl* pBeatLoopControl) {
slotReloopExit(1);
}
+void LoopingControl::slotBeatLoopDeactivateRoll(BeatLoopingControl* pBeatLoopControl) {
+ slotReloopExit(1);
+ m_pSlipEnabled->set(0);
+}
+
void LoopingControl::clearActiveBeatLoop() {
if (m_pActiveBeatLoop != NULL) {
m_pActiveBeatLoop->deactivate();
@@ -654,6 +676,13 @@
connect(m_pToggle, SIGNAL(valueChanged(double)),
this, SLOT(slotToggle(double)),
Qt::DirectConnection);
+
+ // A push-button which activates rolling beatloops
+ m_pActivateRoll = new ControlPushButton(
+ keyForControl(pGroup, "beatlooproll_%1_activate", size));
+ connect(m_pActivateRoll, SIGNAL(valueChanged(double)),
+ this, SLOT(slotActivateRoll(double)),
+ Qt::DirectConnection);
// An indicator control which is 1 if the beatloop is enabled and 0 if not.
m_pEnabled = new ControlObject(
@@ -696,6 +725,15 @@
emit(activateBeatLoop(this));
}
+void BeatLoopingControl::slotActivateRoll(double v) {
+ //qDebug() << "slotActivateRoll" << m_dBeatLoopSize << "v" << v;
+ if (v > 0) {
+ emit(activateBeatLoopRoll(this));
+ } else {
+ emit(deactivateBeatLoopRoll(this));
+ }
+}
+
void BeatLoopingControl::slotToggle(double v) {
//qDebug() << "slotToggle" << m_dBeatLoopSize << "v" << v;
if (!v) {
=== modified file 'mixxx/src/engine/loopingcontrol.h'
--- mixxx/src/engine/loopingcontrol.h 2011-12-22 08:17:26 +0000
+++ mixxx/src/engine/loopingcontrol.h 2012-06-02 21:15:29 +0000
@@ -67,7 +67,9 @@
// beatslicing effect.
void slotBeatLoop(double loopSize, bool keepStartPoint=false);
void slotBeatLoopActivate(BeatLoopingControl* pBeatLoopControl);
+ void slotBeatLoopActivateRoll(BeatLoopingControl* pBeatLoopControl);
void slotBeatLoopDeactivate(BeatLoopingControl* pBeatLoopControl);
+ void slotBeatLoopDeactivateRoll(BeatLoopingControl* pBeatLoopControl);
void slotLoopScale(double);
void slotLoopDouble(double);
@@ -86,6 +88,7 @@
ControlObject* m_pCOLoopScale;
ControlPushButton* m_pLoopHalveButton;
ControlPushButton* m_pLoopDoubleButton;
+ ControlObject* m_pSlipEnabled;
bool m_bLoopingEnabled;
int m_iLoopEndSample;
@@ -124,11 +127,14 @@
public slots:
void slotLegacy(double value);
void slotActivate(double value);
+ void slotActivateRoll(double value);
void slotToggle(double value);
signals:
void activateBeatLoop(BeatLoopingControl*);
void deactivateBeatLoop(BeatLoopingControl*);
+ void activateBeatLoopRoll(BeatLoopingControl*);
+ void deactivateBeatLoopRoll(BeatLoopingControl*);
private:
// Used simply to generate the beatloop_%SIZE and beatseek_%SIZE CO
@@ -138,6 +144,7 @@
bool m_bActive;
ControlPushButton* m_pLegacy;
ControlPushButton* m_pActivate;
+ ControlPushButton* m_pActivateRoll;
ControlPushButton* m_pToggle;
ControlObject* m_pEnabled;
};
=== modified file 'mixxx/src/engine/readaheadmanager.cpp'
--- mixxx/src/engine/readaheadmanager.cpp 2011-12-22 22:25:45 +0000
+++ mixxx/src/engine/readaheadmanager.cpp 2012-06-05 04:42:25 +0000
@@ -4,15 +4,19 @@
#include
#include "engine/readaheadmanager.h"
+#include "sampleutil.h"
#include "mathstuff.h"
#include "engine/enginecontrol.h"
#include "cachingreader.h"
-
ReadAheadManager::ReadAheadManager(CachingReader* pReader) :
m_iCurrentPosition(0),
+ m_pCrossFadeBuffer(new CSAMPLE[MAX_BUFFER_LEN]),
m_pReader(pReader) {
+
+ //zero out crossfade buffer
+ SampleUtil::applyGain(m_pCrossFadeBuffer, 0.0, MAX_BUFFER_LEN);
}
ReadAheadManager::~ReadAheadManager() {
@@ -35,6 +39,7 @@
next_loop.second = m_sEngineControls[0]->nextTrigger(dRate,
m_iCurrentPosition,
0, 0);
+ int preloop_samples = 0;
if (next_loop.second != kNoTrigger) {
int samples_available;
@@ -45,23 +50,24 @@
}
samples_needed = math_max(0, math_min(samples_needed,
samples_available));
+ if (in_reverse) {
+ preloop_samples = m_iCurrentPosition - next_loop.second;
+ } else {
+ preloop_samples = next_loop.second - m_iCurrentPosition;
+ }
+
}
if (in_reverse) {
start_sample = m_iCurrentPosition - samples_needed;
- /*if (start_sample < 0) {
- samples_needed = math_max(0, samples_needed + start_sample);
- start_sample = 0;
- }*/
}
// Sanity checks
- //Q_ASSERT(start_sample >= 0);
Q_ASSERT(samples_needed >= 0);
int samples_read = m_pReader->read(start_sample, samples_needed,
base_buffer);
-
+
if (samples_read != samples_needed)
qDebug() << "didn't get what we wanted" << samples_read << samples_needed;
@@ -85,10 +91,42 @@
if (loop_target != kNoTrigger &&
((in_reverse && m_iCurrentPosition <= loop_trigger) ||
(!in_reverse && m_iCurrentPosition >= loop_trigger))) {
+
m_iCurrentPosition = loop_target;
+
+ if (in_reverse) {
+ m_iCurrentPosition += preloop_samples;
+ } else {
+ m_iCurrentPosition -= preloop_samples;
+ }
+
+ int looping_samples_read = m_pReader->read(m_iCurrentPosition, samples_read,
+ m_pCrossFadeBuffer);
+
+ if (looping_samples_read != samples_read) {
+ qDebug() << "ERROR: Couldn't get samples for crossfade? (should assert?)";
+ }
+
+ // Add log entry so we don't notify seeks
+ if (in_reverse) {
+ m_iCurrentPosition -= looping_samples_read;
+ } else {
+ m_iCurrentPosition += looping_samples_read;
+ }
+ addReadLogEntry(loop_target, m_iCurrentPosition);
+
+ //do crossfade from the current buffer into the new loop beginning
+ double mix_amount = 0.0;
+ double mix_inc = 2.0 / static_cast(samples_read);
+ for (int i=0; i m_readAheadLog;
int m_iCurrentPosition;
CachingReader* m_pReader;
+ CSAMPLE *m_pCrossFadeBuffer;
};
#endif // READAHEADMANGER_H