diff --git a/CHANGELOG b/CHANGELOG index 4a0a0ebb..84e52efc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,21 @@ 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-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 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..6fd63f13 100644 --- a/sources/Application/Instruments/SampleInstrument.cpp +++ b/sources/Application/Instruments/SampleInstrument.cpp @@ -231,46 +231,53 @@ 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: - - // 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 ; - } + 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_ < 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_ ; @@ -287,38 +294,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) { + 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,16 +420,15 @@ void SampleInstrument::updateFeedback(renderParams *rp) { switch(loopMode) { case SILM_ONESHOT: case SILM_LOOP: - case SILM_LOOP_PINGPONG: - case SILM_SLICE: - 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 ; + 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; case SILM_OSC: // case SILM_OSCFINE: if (offset<0x80) { @@ -662,99 +654,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",