5 changes: 5 additions & 0 deletions clients/softcut_jack_osc/src/OscInterface.cpp
Expand Up @@ -209,6 +209,11 @@ void OscInterface::addServerMethods() {
Commands::softcutCommands.post(Commands::Id::SET_CUT_REC_FLAG, argv[0]->i, argv[1]->f);
});

addServerMethod("/set/param/cut/rec_once", "if", [](lo_arg **argv, int argc) {
if (argc < 2) { return; }
Commands::softcutCommands.post(Commands::Id::SET_CUT_REC_ONCE, argv[0]->i, argv[1]->f);
});

addServerMethod("/set/param/cut/play_flag", "if", [](lo_arg **argv, int argc) {
if (argc < 2) { return; }
Commands::softcutCommands.post(Commands::Id::SET_CUT_PLAY_FLAG, argv[0]->i, argv[1]->f);
Expand Down
6 changes: 6 additions & 0 deletions softcut-lib/include/softcut/ReadWriteHead.h
Expand Up @@ -33,6 +33,9 @@ namespace softcut {
void setLoopEndSeconds(float x);
void setFadeTime(float secs);
void setLoopFlag(bool val);
void setRecOnceFlag(bool val);
bool getRecOnceDone();
bool getRecOnceActive();

// set amplitudes
void setRec(float x);
Expand Down Expand Up @@ -80,6 +83,9 @@ namespace softcut {
bool loopFlag; // set to loop, unset for 1-shot
float pre; // pre-record level
float rec; // record level
bool recOnceFlag; // set to record one full loop
bool recOnceDone; // triggers done to tell voice to unset rec flag
int recOnceHead; // keeps track of which subhead is writing

rate_t rate; // current rate
TestBuffers testBuf;
Expand Down
4 changes: 4 additions & 0 deletions softcut-lib/include/softcut/Softcut.h
Expand Up @@ -69,6 +69,10 @@ namespace softcut {
scv[voice].setRecFlag(val);
}

void setRecOnceFlag(int voice, bool val) {
scv[voice].setRecOnceFlag(val);
}

void setPlayFlag(int voice, bool val) {
scv[voice].setPlayFlag(val);
}
Expand Down
2 changes: 2 additions & 0 deletions softcut-lib/include/softcut/Voice.h
Expand Up @@ -40,6 +40,8 @@ namespace softcut {

void setRecFlag(bool val);

void setRecOnceFlag(bool val);

void setPlayFlag(bool val);

void setPreFilterFc(float);
Expand Down
44 changes: 40 additions & 4 deletions softcut-lib/src/ReadWriteHead.cpp
Expand Up @@ -25,6 +25,7 @@ void ReadWriteHead::init(FadeCurves *fc) {
queuedCrossfadeFlag = false;
head[0].init(fc);
head[1].init(fc);
setRecOnceFlag(false);
}

void ReadWriteHead::processSample(sample_t in, sample_t *out) {
Expand All @@ -33,8 +34,14 @@ void ReadWriteHead::processSample(sample_t in, sample_t *out) {

// assert(!(head[0].state_ == Playing && head[1].state_ == Playing) /*multiple active heads*/);

head[0].poke(in, pre, rec);
head[1].poke(in, pre, rec);
if (recOnceFlag || recOnceDone || (recOnceHead > -1)) {
if (recOnceHead > -1) {
head[recOnceHead].poke(in, pre, rec);
}
} else {
head[0].poke(in, pre, rec);
head[1].poke(in, pre, rec);
}

takeAction(head[0].updatePhase(start, end, loopFlag));
takeAction(head[1].updatePhase(start, end, loopFlag));
Expand All @@ -50,8 +57,14 @@ void ReadWriteHead::processSampleNoRead(sample_t in, sample_t *out) {

// assert(!(head[0].state_ == Playing && head[1].state_ == Playing) /*multiple active heads*/);

head[0].poke(in, pre, rec);
head[1].poke(in, pre, rec);
if (recOnceFlag || recOnceDone || (recOnceHead > -1)) {
if (recOnceHead > -1) {
head[recOnceHead].poke(in, pre, rec);
}
} else {
head[0].poke(in, pre, rec);
head[1].poke(in, pre, rec);
}

takeAction(head[0].updatePhase(start, end, loopFlag));
takeAction(head[1].updatePhase(start, end, loopFlag));
Expand Down Expand Up @@ -142,6 +155,15 @@ void ReadWriteHead::cutToPhase(phase_t pos) {
head[active].setState(State::FadeOut);
}

if (recOnceHead==newActive) {
recOnceHead = -1;
recOnceDone = true;
}
if (recOnceFlag) {
recOnceFlag = false;
recOnceHead = newActive;
}

head[newActive].setState(State::FadeIn);
head[newActive].setPhase(pos);

Expand Down Expand Up @@ -169,6 +191,20 @@ void ReadWriteHead::setLoopFlag(bool val) {
loopFlag = val;
}

void ReadWriteHead::setRecOnceFlag(bool val) {
recOnceFlag = val;
recOnceDone = false;
recOnceHead = -1;
}

bool ReadWriteHead::getRecOnceDone() {
return recOnceDone;
}

bool ReadWriteHead::getRecOnceActive() {
return (recOnceDone || recOnceFlag || recOnceHead>-1);
}

void ReadWriteHead::setSampleRate(float sr_) {
sr = sr_;
head[0].setSampleRate(sr);
Expand Down
22 changes: 22 additions & 0 deletions softcut-lib/src/Voice.cpp
Expand Up @@ -95,6 +95,15 @@ void Voice:: processBlockMono(const float *in, float *out, int numFrames) {


rawPhase.store(sch.getActivePhase(), std::memory_order_relaxed);

if(recFlag) {
if (sch.getRecOnceDone()) {
// record once is finished, turn off recording flag
// and reset the recording subheads
recFlag = false;
sch.setRecOnceFlag(false);
}
}
}

void Voice::setSampleRate(float hz) {
Expand Down Expand Up @@ -147,6 +156,12 @@ void Voice::setRecFlag(bool val) {
}
}
recFlag = val;
if (!val) {
// turn off rec once if active
if (sch.getRecOnceActive()) {
sch.setRecOnceFlag(false);
}
}
}

void Voice::setPlayFlag(bool val) {
Expand Down Expand Up @@ -235,6 +250,13 @@ void Voice::setPostFilterDry(float x) {
svfPostDryLevel = x;
}

void Voice::setRecOnceFlag(bool val) {
sch.setRecOnceFlag(val);
if (val) {
setRecFlag(true);
}
}

void Voice::setBuffer(float *b, unsigned int nf) {
buf = b;
bufFrames = nf;
Expand Down