From f4a394bb44a03da18090f31ece78f79a191bba7c Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Fri, 24 Apr 2026 17:39:44 +0000 Subject: [PATCH 1/6] Slices decoupled from loop mode Enables sliced loops Slice count greater than 1 assumes slice mode --- CHANGELOG | 5 + docs/wiki/tips_and_tricks.md | 14 +- .../Instruments/SampleInstrument.cpp | 240 +++++++++--------- .../Instruments/SampleInstrument.h | 18 +- .../Instruments/SampleInstrumentDatas.h | 13 +- sources/Application/Model/Project.h | 2 +- 6 files changed, 147 insertions(+), 145 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4a0a0ebb..29edf3de 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,11 @@ 1.6.0-bacon10 Allow greater granular stretching using LPOF Documentation in tips and tricks + Slices decoupled from loop mode + * Slices are no longer a loop mode — they are now an independent parameter + * Set slices > 1 to enable slicing; loop mode (none/loop/ping pong/etc.) applies within each slice + * Enables looped slices, e.g. for amen breaks and other timed loops + * Old projects with "slicer" loop mode load correctly: slices count is preserved, loop mode resets to "none" 1.6.0-bacon5 Auto-load last project on startup * Application can now load the last project automatically on startup diff --git a/docs/wiki/tips_and_tricks.md b/docs/wiki/tips_and_tricks.md index 8699ac18..9907c167 100644 --- a/docs/wiki/tips_and_tricks.md +++ b/docs/wiki/tips_and_tricks.md @@ -100,12 +100,14 @@ Here's a nice example, courtesy of jonbro, chopping drums he had recorded previo [Jonbro - the thing is the thing](https://battleofthebits.com/arena/Entry/the+thing+is+the+thing/1479/) -### Slice! (new chopping method) -Since version 1.3o there is a new loop mode called Slice -Using this, you can assign up to 256 individual slices from C-2 (C minus two) up to the amount of slices you set. How handy! - -Of course, you are not limited to drum loops. Just chop anything away ! -The example project BETA uses Slice mode to chop up a dank AF sample by Basscarrier +### Slices (sample chopping) +Set the **slices** parameter in the instrument to divide a sample into equal parts, +mapped chromatically from C-2 (the lowest note, not C2) upward. The **loop mode** parameter then applies +independently within each slice — set it to "loop" or "ping pong" to get looped +slices, great for amen breaks and timed loops. Set to Oscillator for very grainy rhythmic noises + +You can assign up to 256 individual slices. Of course, you are not limited to drum +loops — just chop anything away! ### Granular timestretching with LPOF diff --git a/sources/Application/Instruments/SampleInstrument.cpp b/sources/Application/Instruments/SampleInstrument.cpp index 29beb92c..9c09b19d 100644 --- a/sources/Application/Instruments/SampleInstrument.cpp +++ b/sources/Application/Instruments/SampleInstrument.cpp @@ -230,12 +230,28 @@ bool SampleInstrument::Start(int channel,unsigned char midinote,bool cleanstart) }*/ rp->reverse_=false ; - float driverRate = float(Audio::GetInstance()->GetSampleRate()); - - switch (loopmode) { - case SILM_ONESHOT: - case SILM_LOOP: - case SILM_LOOP_PINGPONG: + float driverRate=float(Audio::GetInstance()->GetSampleRate()) ; + int isSliced = slices_->GetInt() > 1; + if (isSliced) { + if (rp->midiNote_ > slices_->GetInt() - 1) return false; // No sound outside of slice range + int slice = rp->rendLoopEnd_ / slices_->GetInt(); + rp->rendLoopStart_ = rp->midiNote_ * slice; + rp->rendLoopEnd_ = (rp->midiNote_ + 1) * slice; + } + + switch (loopmode) { + case SILM_ONESHOT: + case SILM_LOOP: + case SILM_LOOP_PINGPONG: + + // Compute speed factor + // if instrument sampled below 44.1Khz, should + // travel slower in sample + + rp->rendFirst_ = (isSliced) ? rp->rendLoopStart_ : start_->GetInt(); + rp->position_= float(rp->rendFirst_); + rp->baseSpeed_=fl2fp(source_->GetSampleRate(rp->midiNote_)/driverRate) ; + rp->reverse_=(rp->rendLoopEnd_position_) ; // Compute speed factor // if instrument sampled below 44.1Khz, should @@ -287,33 +303,24 @@ bool SampleInstrument::Start(int channel,unsigned char midinote,bool cleanstart) rp->position_= float(rp->rendFirst_); } break ; - } - case SILM_SLICE: { - if (rp->midiNote_ > slices_->GetInt()-1) break; // No sound outside of slice range - int note = rp->midiNote_; - int wavSize=rp->rendLoopEnd_; - int slice = wavSize/slices_->GetInt(); - - rp->position_= float(note*slice); - rp->baseSpeed_=fl2fp(source_->GetSampleRate(rp->midiNote_)/driverRate) ; - rp->speed_ = rp->baseSpeed_; - rp->rendLoopEnd_ = (note+1)*slice; - break ; - } - case SILM_LAST: + } + case SILM_LAST: NAssert(0) ; break ; } // Compute octave & note difference from root - float fineTune = float(fineTune_->GetInt() - 0x7F); - fineTune /= float(0x80); - int offset = midinote - rootNote; - if (loopmode == SILM_SLICE) { - offset = rootNote - source_->GetRootNote(rp->midiNote_); - } - while (offset>127) + + // Compute octave & note difference from root + + float fineTune=float(fineTune_->GetInt()-0x7F) ; + fineTune/=float(0x80) ; + int offset=midinote-rootNote ; + if (isSliced) { + offset = rootNote-source_->GetRootNote(rp->midiNote_); + } + while (offset>127) { offset-=12 ; } @@ -427,9 +434,8 @@ void SampleInstrument::updateFeedback(renderParams *rp) { switch(loopMode) { case SILM_ONESHOT: case SILM_LOOP: - case SILM_LOOP_PINGPONG: - case SILM_SLICE: - case SILM_LOOPSYNC: + case SILM_LOOP_PINGPONG: + case SILM_LOOPSYNC: rp->feedbackMode_=FB_ADD ; if (offset<0x80) { offset=FB_BUFFER_LENGTH-offset-1 ; @@ -662,99 +668,97 @@ bool SampleInstrument::Render(int channel,fixed *buffer,int size,bool updateTick if (!rpReverse) { //Looping forward if (input>=lastSample/*-((loopMode==SILM_OSCFINE)?1:0)*/) { switch(loopMode) { - case SILM_ONESHOT: - case SILM_SLICE: - *rpFinished=true ; - break ; - case SILM_LOOP: - case SILM_OSC: - case SILM_LOOPSYNC: - input=loopPosition ; - rpReverse=(loopPosition>lastSample) ; - if (rpReverse) { - fpSpeed=-rp->speed_ ; - } else { - fpSpeed=rp->speed_ ; - } - break ; - case SILM_LOOP_PINGPONG: - if ((loopPosition > lastSample)) { - if (input <= lastSample || input >= loopPosition) { - rpReverse = !rpReverse; - fpSpeed = -fpSpeed; - } - } else { - if (input>=lastSample || input <= loopPosition) { - rpReverse = !rpReverse; - fpSpeed = -fpSpeed; - } - } - break; -/* case SILM_OSCFINE: - { - int offset=(input-lastSample)/channelCount ; - rpReverse=(loopPosition>lastSample) ; - if (rpReverse) { - fpSpeed=-rp->speed_ ; - input=loopPosition-offset ; - } else { - fpSpeed=rp->speed_ ; - input=loopPosition+offset ; - } - break ; - }*/ - case SILM_LAST: - NAssert(0) ; - break ; + case SILM_ONESHOT: + *rpFinished = true; + break; + case SILM_LOOP: + case SILM_OSC: + case SILM_LOOPSYNC: + input = loopPosition; + rpReverse = (loopPosition > lastSample); + if (rpReverse) { + fpSpeed = -rp->speed_; + } else { + fpSpeed = rp->speed_; + } + break; + case SILM_LOOP_PINGPONG: + if ((loopPosition > lastSample)) { + if (input <= lastSample || input >= loopPosition) { + rpReverse = !rpReverse; + fpSpeed = -fpSpeed; + } + } else { + if (input >= lastSample || input <= loopPosition) { + rpReverse = !rpReverse; + fpSpeed = -fpSpeed; + } + } + break; + /* case SILM_OSCFINE: + { + int + offset=(input-lastSample)/channelCount ; + rpReverse=(loopPosition>lastSample) + ; if (rpReverse) { fpSpeed=-rp->speed_ ; + input=loopPosition-offset + ; } else { fpSpeed=rp->speed_ ; + input=loopPosition+offset + ; + } + break ; + }*/ + case SILM_LAST: + NAssert(0); + break; } ; } } else { // Looping backward if (inputlastSample) ; - if (rpReverse) { - fpSpeed=-rp->speed_ ; - } else { - fpSpeed=rp->speed_ ; - } - break ; - case SILM_LOOP_PINGPONG: - if ((loopPosition > lastSample)) { - if (input <= lastSample || input >= loopPosition) { - rpReverse = !rpReverse; - fpSpeed = -fpSpeed; - } - } else { - if (input>=lastSample || input <= loopPosition) { - rpReverse = !rpReverse; - fpSpeed = -fpSpeed; - } - } - break; -/* case SILM_OSCFINE: - { - int offset=(lastSample-input)/channelCount ; - rpReverse=(loopPosition>lastSample) ; - if (rpReverse) { - fpSpeed=-rp->speed_ ; - input=loopPosition-offset ; - } else { - fpSpeed=rp->speed_ ; - input=loopPosition+offset ; - } - break ; - }*/ - case SILM_LAST: - NAssert(0) ; - break ; + case SILM_ONESHOT: + *rpFinished = true; + break; + case SILM_LOOP: + case SILM_OSC: + case SILM_LOOPSYNC: + input = loopPosition; + rpReverse = (loopPosition > lastSample); + if (rpReverse) { + fpSpeed = -rp->speed_; + } else { + fpSpeed = rp->speed_; + } + break; + case SILM_LOOP_PINGPONG: + if ((loopPosition > lastSample)) { + if (input <= lastSample || input >= loopPosition) { + rpReverse = !rpReverse; + fpSpeed = -fpSpeed; + } + } else { + if (input >= lastSample || input <= loopPosition) { + rpReverse = !rpReverse; + fpSpeed = -fpSpeed; + } + } + break; + /* case SILM_OSCFINE: + { + int + offset=(lastSample-input)/channelCount ; + rpReverse=(loopPosition>lastSample) + ; if (rpReverse) { fpSpeed=-rp->speed_ ; + input=loopPosition-offset + ; } else { fpSpeed=rp->speed_ ; + input=loopPosition+offset + ; + } + break ; + }*/ + case SILM_LAST: + NAssert(0); + break; } ; } }; diff --git a/sources/Application/Instruments/SampleInstrument.h b/sources/Application/Instruments/SampleInstrument.h index bee6c4d2..eadefd43 100644 --- a/sources/Application/Instruments/SampleInstrument.h +++ b/sources/Application/Instruments/SampleInstrument.h @@ -12,16 +12,14 @@ #include "Foundation/Variables/WatchedVariable.h" enum SampleInstrumentLoopMode { - SILM_ONESHOT=0, - SILM_LOOP, - SILM_LOOP_PINGPONG, - SILM_OSC, -// SILM_OSCFINE, - SILM_LOOPSYNC, - SILM_SLICE, - SILM_LAST -} ; - + SILM_ONESHOT = 0, + SILM_LOOP, + SILM_LOOP_PINGPONG, + SILM_OSC, + // SILM_OSCFINE, + SILM_LOOPSYNC, + SILM_LAST +}; #define NO_SAMPLE (-1) #define SIP_VOLUME MAKE_FOURCC('V','O','L','M') diff --git a/sources/Application/Instruments/SampleInstrumentDatas.h b/sources/Application/Instruments/SampleInstrumentDatas.h index f32c17a1..9b873937 100644 --- a/sources/Application/Instruments/SampleInstrumentDatas.h +++ b/sources/Application/Instruments/SampleInstrumentDatas.h @@ -1,15 +1,8 @@ #define SEMITONE_FREQ_INTERVAL 1.0594630943592952645618252949461F - -char *loopTypes[SILM_LAST]= { - "none", - "loop", - "ping pong", - "oscillator", -// "oscillator fine", - "looper sync", - "slicer" -} ; +char *loopTypes[SILM_LAST] = {"none", "loop", "ping pong", "oscillator", + // "oscillator fine", + "looper sync"}; char *interpolationTypes[] = { "linear", diff --git a/sources/Application/Model/Project.h b/sources/Application/Model/Project.h index 5dea7d7b..28e178ec 100644 --- a/sources/Application/Model/Project.h +++ b/sources/Application/Model/Project.h @@ -21,7 +21,7 @@ #define PROJECT_NUMBER "1" #define PROJECT_RELEASE "6" -#define BUILD_COUNT "0-bacon11" +#define BUILD_COUNT "0-bacon10" #define MAX_TAP 3 From 34ab824ceb17a7f895997a42c1f6d60b4c33d356 Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Fri, 24 Apr 2026 19:53:47 +0000 Subject: [PATCH 2/6] clang-format --- sources/Application/Instruments/SampleInstrument.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sources/Application/Instruments/SampleInstrument.cpp b/sources/Application/Instruments/SampleInstrument.cpp index 9c09b19d..4ebc52e9 100644 --- a/sources/Application/Instruments/SampleInstrument.cpp +++ b/sources/Application/Instruments/SampleInstrument.cpp @@ -230,7 +230,7 @@ bool SampleInstrument::Start(int channel,unsigned char midinote,bool cleanstart) }*/ rp->reverse_=false ; - float driverRate=float(Audio::GetInstance()->GetSampleRate()) ; + float driverRate = float(Audio::GetInstance()->GetSampleRate()); int isSliced = slices_->GetInt() > 1; if (isSliced) { if (rp->midiNote_ > slices_->GetInt() - 1) return false; // No sound outside of slice range @@ -314,11 +314,11 @@ bool SampleInstrument::Start(int channel,unsigned char midinote,bool cleanstart) // Compute octave & note difference from root - float fineTune=float(fineTune_->GetInt()-0x7F) ; - fineTune/=float(0x80) ; - int offset=midinote-rootNote ; - if (isSliced) { - offset = rootNote-source_->GetRootNote(rp->midiNote_); + float fineTune = float(fineTune_->GetInt() - 0x7F); + fineTune /= float(0x80); + int offset = midinote - rootNote; + if (isSliced) { + offset = rootNote - source_->GetRootNote(rp->midiNote_); } while (offset>127) { From ed2500cc9db6eef1aea48650c280d266a74d2aea Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Fri, 24 Apr 2026 21:42:30 +0200 Subject: [PATCH 3/6] Fixes wonky behavior when LPOF in tables (#245) Now possible to granularly stretch and compress with LPOF to a greater degree Fixes https://github.com/djdiskmachine/LittleGPTracker/issues/243 --- CHANGELOG | 4 ++++ sources/Application/Model/Project.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 29edf3de..85dde262 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,11 +1,15 @@ 1.6.0-bacon10 Allow greater granular stretching using LPOF Documentation in tips and tricks +1.6.0-bacon11 Slices decoupled from loop mode * Slices are no longer a loop mode — they are now an independent parameter * Set slices > 1 to enable slicing; loop mode (none/loop/ping pong/etc.) applies within each slice * Enables looped slices, e.g. for amen breaks and other timed loops * Old projects with "slicer" loop mode load correctly: slices count is preserved, loop mode resets to "none" +1.6.0-bacon10 + Allow greater granular stretching using LPOF + Documentation in tips and tricks 1.6.0-bacon5 Auto-load last project on startup * Application can now load the last project automatically on startup diff --git a/sources/Application/Model/Project.h b/sources/Application/Model/Project.h index 28e178ec..5dea7d7b 100644 --- a/sources/Application/Model/Project.h +++ b/sources/Application/Model/Project.h @@ -21,7 +21,7 @@ #define PROJECT_NUMBER "1" #define PROJECT_RELEASE "6" -#define BUILD_COUNT "0-bacon10" +#define BUILD_COUNT "0-bacon11" #define MAX_TAP 3 From 5d42f248d47a5974cdc556df805ed86b7df57bcb Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Fri, 24 Apr 2026 17:39:44 +0000 Subject: [PATCH 4/6] Slices decoupled from loop mode Enables sliced loops Slice count greater than 1 assumes slice mode --- CHANGELOG | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 85dde262..84e52efc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,12 @@ 1.6.0-bacon10 Allow greater granular stretching using LPOF Documentation in tips and tricks +1.6.0-bacon10 + Slices decoupled from loop mode + * Slices are no longer a loop mode — they are now an independent parameter + * Set slices > 1 to enable slicing; loop mode (none/loop/ping pong/etc.) applies within each slice + * Enables looped slices, e.g. for amen breaks and other timed loops + * Old projects with "slicer" loop mode load correctly: slices count is preserved, loop mode resets to "none" 1.6.0-bacon5 Auto-load last project on startup * Application can now load the last project automatically on startup From b067cdaf7d007eb21df0955fd58f2d10588efa7b Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Sat, 25 Apr 2026 17:34:50 +0000 Subject: [PATCH 5/6] Fix merge conflict drop of essential code Had dropped setting sample start offset --- .../Instruments/SampleInstrument.cpp | 81 +++++++++---------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/sources/Application/Instruments/SampleInstrument.cpp b/sources/Application/Instruments/SampleInstrument.cpp index 4ebc52e9..e9d780a8 100644 --- a/sources/Application/Instruments/SampleInstrument.cpp +++ b/sources/Application/Instruments/SampleInstrument.cpp @@ -242,51 +242,42 @@ bool SampleInstrument::Start(int channel,unsigned char midinote,bool cleanstart) switch (loopmode) { case SILM_ONESHOT: case SILM_LOOP: - case SILM_LOOP_PINGPONG: - - // Compute speed factor - // if instrument sampled below 44.1Khz, should - // travel slower in sample - - rp->rendFirst_ = (isSliced) ? rp->rendLoopStart_ : start_->GetInt(); - rp->position_= float(rp->rendFirst_); - rp->baseSpeed_=fl2fp(source_->GetSampleRate(rp->midiNote_)/driverRate) ; - rp->reverse_=(rp->rendLoopEnd_position_) ; - - // Compute speed factor - // if instrument sampled below 44.1Khz, should - // travel slower in sample - - rp->rendFirst_ = start_->GetInt(); - rp->position_ = float(rp->rendFirst_); - rp->baseSpeed_ = - fl2fp(source_->GetSampleRate(rp->midiNote_) / driverRate); - rp->reverse_ = (rp->rendLoopEnd_ < rp->position_); - - break; - - case SILM_OSC: - // case SILM_OSCFINE: - { - - float freq = 261.6255653006f; // C3 - /* if (loopmode==SILM_OSCFINE) { - freq=float(pow(2.0,-0.75))*440; // C3 - }*/ - int length = rp->rendLoopEnd_ - rp->rendLoopStart_; - if (length == 0) - length = 1; - if (length < 0) { - rp->reverse_=true ; - length=-length ; - } ; - rp->baseSpeed_=fl2fp((freq*length)/driverRate) ; - rp->rendFirst_ = rp->rendLoopStart_; - if (cleanstart) { - rp->position_= float(rp->rendFirst_); - } - break ; - } + case SILM_LOOP_PINGPONG: + // Compute speed factor + // if instrument sampled below 44.1Khz, should + // travel slower in sample + + rp->rendFirst_ = + (isSliced) ? rp->rendLoopStart_ : start_->GetInt(); + rp->position_ = float(rp->rendFirst_); + rp->baseSpeed_ = + fl2fp(source_->GetSampleRate(rp->midiNote_) / driverRate); + rp->reverse_ = (rp->rendLoopEnd_ < rp->position_); + + break; + + case SILM_OSC: + // case SILM_OSCFINE: + { + + float freq = 261.6255653006f; // C3 + /* if (loopmode==SILM_OSCFINE) { + freq=float(pow(2.0,-0.75))*440; // C3 + }*/ + int length = rp->rendLoopEnd_ - rp->rendLoopStart_; + if (length == 0) + length = 1; + if (length < 0) { + rp->reverse_ = true; + length = -length; + }; + rp->baseSpeed_ = fl2fp((freq * length) / driverRate); + rp->rendFirst_ = rp->rendLoopStart_; + if (cleanstart) { + rp->position_ = float(rp->rendFirst_); + } + break; + } case SILM_LOOPSYNC: { int length=rp->rendLoopEnd_-rp->rendLoopStart_ ; From 05a5af4907275a5fc0b03a3ce2c1f008a1813c28 Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Sat, 25 Apr 2026 19:17:01 +0000 Subject: [PATCH 6/6] clang-format --- .../Instruments/SampleInstrument.cpp | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/sources/Application/Instruments/SampleInstrument.cpp b/sources/Application/Instruments/SampleInstrument.cpp index e9d780a8..6fd63f13 100644 --- a/sources/Application/Instruments/SampleInstrument.cpp +++ b/sources/Application/Instruments/SampleInstrument.cpp @@ -301,22 +301,17 @@ bool SampleInstrument::Start(int channel,unsigned char midinote,bool cleanstart) } // Compute octave & note difference from root - - - // Compute octave & note difference from root - - float fineTune = float(fineTune_->GetInt() - 0x7F); - fineTune /= float(0x80); - int offset = midinote - rootNote; - if (isSliced) { - offset = rootNote - source_->GetRootNote(rp->midiNote_); + float fineTune = float(fineTune_->GetInt() - 0x7F); + fineTune /= float(0x80); + int offset = midinote - rootNote; + if (isSliced) { + offset = rootNote - source_->GetRootNote(rp->midiNote_); } - while (offset>127) - { - offset-=12 ; - } - - fixed freqFactor=fl2fp(float(pow(2.0,(offset+fineTune)/12.0))) ; + while (offset > 127) { + offset -= 12; + } + + fixed freqFactor=fl2fp(float(pow(2.0,(offset+fineTune)/12.0))) ; rp->baseSpeed_=fp_mul(rp->baseSpeed_,freqFactor) ; rp->speed_=rp->baseSpeed_ ; @@ -427,13 +422,13 @@ void SampleInstrument::updateFeedback(renderParams *rp) { case SILM_LOOP: case SILM_LOOP_PINGPONG: case SILM_LOOPSYNC: - rp->feedbackMode_=FB_ADD ; - if (offset<0x80) { - offset=FB_BUFFER_LENGTH-offset-1 ; - } else { - offset=FB_BUFFER_LENGTH-offset*10-1 ; - } ; - break ; + rp->feedbackMode_ = FB_ADD; + if (offset < 0x80) { + offset = FB_BUFFER_LENGTH - offset - 1; + } else { + offset = FB_BUFFER_LENGTH - offset * 10 - 1; + } + break; case SILM_OSC: // case SILM_OSCFINE: if (offset<0x80) {