Skip to content

Commit

Permalink
TB-3PO: Cleaned up code & comments with some minor CV handling tweaki…
Browse files Browse the repository at this point in the history
…ng to give a bit more range to density and ensure symmetry for +- values. Cleaned up docs and updated for new features. Added a help page documenting the changes to the core Hemisphere suite. Version 1.9 ready
  • Loading branch information
Logarhythm1 committed Jul 6, 2020
1 parent 2194ab5 commit 37a7a5b
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 106 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion help_docs/applet_TB-3PO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ TB-3PO is a TB-303 style, pitch CV and gate pattern generator robot, capable of
Controls

- Digital Ins: 1:Clock, 2:Reset / Regenerate
- CV Ins: 1:Density, 2:Transpose
- CV Ins: 1:Transpose, 2:Density (Center point)
- CV Outs: A:V/Oct CV + glide, B:Gate (3v normal, 5v Accent)
- Encoder: Sets the seed as unlocked or locked, edits unlocked seeds, sets note/pitch density, quantization scale, root note, and number of steps in the pattern.

Expand Down Expand Up @@ -35,6 +35,11 @@ A bi-directional 'density' control specifies how many of the pattern's steps are

Density changes are applied on every clock, but if the seed is locked, specific values of density will give the same subset of patterns. In this way you can break down from busy to sparse and back.

### Density CV
When the cursor is on Density, it can be changed directly by turning the encoder. CV Input 2 applies offsets to this centerpoint value. CV is bipolar, with every +-2.5 volts corresponding to +-7 density steps. Due to the O&C's CV input range of approximately -3v to +6v, this means that there is an effective CV range of about -8 to +15 density values from the encoder-set center point.
Turning the encoder while nonzero CV is applied will show the encoder's value alongside a "knob" icon momentarily, so the offset can be edited precisely even while CV is modulating it.
Note that if the encoder value is set to -7 then the full range of density values is available by applying CV 0-5v.

## Scale
Sets the quantization scale to use for the pattern, and makes only those notes available to the pattern generator. It affects the current output CV immediately and applies to the pattern on the next clock.

Expand All @@ -50,6 +55,8 @@ Like on the TB-303, each step can have Gate, Accent, Glide, +octave or -octave s
## Visualization
- The heart icon will beat whenever the pattern is reset
- The seed die icon will hop whenever a new seed is randomly picked
- A CV icon next to density indicates that a +- offset is being applied to the encoder-set density value.
- A Knob icon next to density indicates that the encoder-set density value is being shown momentarily.
- A single octave keyboard shows the current playing step's pitch, quantized to semitones
- Icons to the keyboard's right:
- "!" Indicates that the current step has an Accent and the gate cv will be 5v instead of 3v
Expand Down
16 changes: 16 additions & 0 deletions help_docs/applet_edits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Changes to the stock firmware:

## Removed Applications:
- Captain MIDI is disabled to make room for new applets.

## Changes to existing Hemisphere Applets:
Logarhythm Branch applies several changes to some of the stock Hemisphere Applets, adding a few new features and some subjective tweaks for preference.

### Shuffle
CV output B (formerly unused) now outputs Triplets. It detects the clock tempo and emits 3 triplet pulses evenly distributed across 4 clocks, starting at the first input clock. The reset input resets this count to 0 along with the shuffle output's odd/even beat. No input parameters affect the triplets-- it's just a bonus output that can be fiddly to patch normally and pairs well with swing.

### ShiftRegister (Turing Machine)
An output range parameter has been added, allowing you to constrain the pitch output range from 1-32 scale notes instead of always having a range of 32. This parameter is not saved due to space restrictions.

### Step5
Holding reset input high will suppress clock triggers, similar to an analogue sequencer.
116 changes: 11 additions & 105 deletions software/o_c_REV/HEM_TB3PO.ino
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,12 @@
// CV output 1 is pitch, CV output 2 is gates
// CV pitch out includes fixed-time exponential pitch slides timed as on 303s
// CV gates are output at 3v for normal notes and 5v for accented notes
// TODO: Output proper envelopes?


#include "braids_quantizer.h"
#include "braids_quantizer_scales.h"
#include "OC_scales.h"

//#define ACID_MAX_STEPS 16
#define ACID_HALF_STEPS 16
#define ACID_MAX_STEPS 32

Expand All @@ -47,7 +45,6 @@ class TB_3PO : public HemisphereApplet

void Start()
{

manual_reset_flag = 0;
rand_apply_anim = 0;
curr_step_semitone = 0;
Expand All @@ -63,7 +60,6 @@ class TB_3PO : public HemisphereApplet
display_semi_quantizer.Configure(OC::Scales::GetScale(OC::Scales::SCALE_SEMI), 0xffff);

density = 12;
//density_cv_lock = 0;
density_encoder_display = 0;

num_steps = 16;
Expand Down Expand Up @@ -109,69 +105,24 @@ class TB_3PO : public HemisphereApplet
step = 0;
}


// Control transpose from cv1 (Very fun to wiggle)
transpose_note_in = 0;
if (DetentedIn(0))
{
transpose_note_in = In(0) / 128; // 128 ADC steps per semitone
}

// Control density from cv1 (Wiggling can build up & break down patterns nicely, especially if seed is locked)
/*
density_cv_lock = 0; // Track if density is under cv control
if (DetentedIn(1))
{
int num = ProportionCV(In(1), 15);
density = constrain(num, 0, 14);
density_cv_lock = 1;
}
*/
// Test: the encoder sets a center point and CV becomes a bipolar offset
// Offset density from its encoder-set value with cv2 (Wiggling can build up & break down patterns nicely, especially if seed is locked)
{

/* Working: +-3v
int signal = constrain(In(1), -HEMISPHERE_3V_CV, HEMISPHERE_3V_CV);
// N.B. Use only positive values for Proportion() here so it is symmetrical (Otherwise negative values will e.g. immediately go to -1 at any value <0v)
density_cv = Proportion(abs(signal), HEMISPHERE_3V_CV, 7);
if(signal < 0)
{
density_cv *= -1; // Restore as negative if necessary
}
*/


/*
// -3 to +6v, giving -7 to +14 added to encoder density value
int signal = constrain(In(1), -HEMISPHERE_3V_CV, HEMISPHERE_3V_CV*2);
if(signal < 0)
{
// N.B. Use only positive values for Proportion() here so it is symmetrical (Otherwise negative values will e.g. immediately go to -1 at any value <0v)
density_cv = Proportion(abs(signal), HEMISPHERE_3V_CV, 7) * -1; // restore negative value
}
else
{
density_cv = Proportion(signal, HEMISPHERE_3V_CV*2, 14); // Span 7 values per 3v
}
*/

// -2.5v to +5v (HEMISPHERE_MAX_CV), giving -7 to +14 added to encoder density value
// -2.5v to +5v (HEMISPHERE_MAX_CV), giving about -8 to +15 added to encoder density value
int signal = constrain(In(1), -HEMISPHERE_3V_CV, HEMISPHERE_MAX_CV); // Allow negative to go about as far as it will reach
if(signal < 0)
density_cv = Proportion(abs(signal), HEMISPHERE_MAX_CV, 15); // Apply proportion uniformly to +- voltages as + for symmetry (Avoids rounding differences)
if(signal <0)
{
// N.B. Use only positive values for Proportion() here so it is symmetrical (Otherwise negative values will e.g. immediately go to -1 at any value <0v)
density_cv = Proportion(abs(signal), HEMISPHERE_MAX_CV/2, 7) * -1; // restore negative value
density_cv *= -1; // Restore negative sign if -v
}
else
{
density_cv = Proportion(signal, HEMISPHERE_MAX_CV, 14); // Span 7 values per 3v
}

//density_cv_lock = density_cv != 0; // Flag density cv active
density = static_cast<uint8_t>(constrain(density_encoder + density_cv, 0, 14));
}


// Wait for the ADC since transpose CV is needed
if (Clock(0))
Expand Down Expand Up @@ -241,9 +192,6 @@ class TB_3PO : public HemisphereApplet
curr_gate_cv = 0;//HEMISPHERE_CENTER_CV;
}
}

// TODO: Apply CV density directly to regenerate gates, if int change?
// TODO: Maybe instead, this cv live-controls a gate skip % chance? (And also sets density when regenerating)

// Update slide if needed
if(curr_pitch_cv != slide_end_cv)
Expand Down Expand Up @@ -288,8 +236,7 @@ class TB_3PO : public HemisphereApplet
// Timesliced generation of new patterns, if triggered
// Do this last to not interfere with the body of the time for this hemisphere's update
// (This is speculation without knowing how to best profile performance on this system)
update_regeneration();

update_regeneration();
}

void View() {
Expand Down Expand Up @@ -432,7 +379,6 @@ class TB_3PO : public HemisphereApplet
int lock_seed; // If 1, the seed won't randomize (and manual editing is enabled)

uint16_t seed; // The random seed that deterministically builds the sequence
//uint16_t current_pattern_seed; // Track the seed value was used to render the current pattern

int scale; // Active quantization & generation scale
uint8_t root; // Root note
Expand All @@ -443,7 +389,6 @@ class TB_3PO : public HemisphereApplet
// change from the prior pitch, giving repeating lines (note: octave jumps still apply)

uint8_t current_pattern_density; // Track what density value was used to generate the current pattern (to detect if regeneration is required)
//uint8_t density_cv_lock; // Tracks if density is under cv control (can't change manually)

// Density controls (Encoder sets center point, CV can apply +-)
int density_encoder; // density value contributed by the encoder (center point)
Expand Down Expand Up @@ -608,13 +553,6 @@ class TB_3PO : public HemisphereApplet
oct_ups = 0;
oct_downs = 0;
}
/*
else
{
// Nudge the existing bitvectors so they're ready to write the next bit
oct_ups <<= 1;
oct_downs <<= 1;
}*/

// Do either the first or second set of steps on this pass
int max_step = (bFirstHalf ? ACID_HALF_STEPS : ACID_MAX_STEPS);
Expand Down Expand Up @@ -657,11 +595,8 @@ class TB_3PO : public HemisphereApplet
scale_size = 12;
}

// Track the seed used to render the current pattern, for change detection
//current_pattern_seed = seed;
current_pattern_scale_size = scale_size;
}


// Change pattern density without affecting pitches
void apply_density()
Expand All @@ -681,17 +616,7 @@ class TB_3PO : public HemisphereApplet
slides = 0;
accents = 0;
}
/*else
{
// Nudge the existing bitvectors so they're ready to write the next bit
// (Note: coupled with correcting the order of bitshift on each below, this emulates the bug that advanced the bitvector one extra in the original version)
// (For the sake of maintaining patterns)
gates <<= 1;
slides <<= 1;
accents <<= 1;
}*/

//for(int i=0; i<ACID_MAX_STEPS; ++i)
// Apply to each step
// Do half of the steps on each pass of this func
for(int i=0; i< ACID_HALF_STEPS; ++i)
Expand All @@ -707,8 +632,7 @@ class TB_3PO : public HemisphereApplet
// Less probability of consecutive accents
accents <<= 1;
latest_accent = rand_bit((latest_accent ? 7 : 16));
accents |= latest_accent;

accents |= latest_accent;
}

// Track the value of density used to render the pattern (to detect changes)
Expand Down Expand Up @@ -755,21 +679,6 @@ class TB_3PO : public HemisphereApplet
return (oct_downs & (0x01 << step_num));
}

/*
// This is wholly unnecessary _except_ for the purpose of keeping legacy pattern seeds'
// generation of 16-step patterns the same as the new 32-step generation
//(by swapping the first 16 bits generated in each bitvector to align with the first 16 steps again)
#define SWAP_2MSB_2LSB(x) ( x = (x << 16) | (x >> 16))
void restore_legacy_byte_orders()
{
SWAP_2MSB_2LSB(gates);
SWAP_2MSB_2LSB(slides);
SWAP_2MSB_2LSB(accents);
SWAP_2MSB_2LSB(oct_ups);
SWAP_2MSB_2LSB(oct_downs);
}
*/

int get_next_step(int step_num)
{
// loop at the current loop point
Expand Down Expand Up @@ -892,8 +801,7 @@ class TB_3PO : public HemisphereApplet
gfxPos(0, 27);gfxPrint(test);
gfxPos(0, 37); gfxPrintVoltage(density_cv);
*/



// Scale and root note select
xd = (scale < 4) ? 32 : 39; // Slide/crowd to the left a bit if showing the "USER1"-"USER4" scales, which are uniquely five instead of four characters
gfxPrint(xd, 27, OC::scale_names_short[scale]);
Expand All @@ -916,15 +824,13 @@ class TB_3PO : public HemisphereApplet
gfxBitmap(41, 54, 8, UP_BTN_ICON);
}

// Constrain to the scale
//gfxPrint(49, 55, curr_step_semitone);

int keyboard_pitch = curr_step_semitone -4; // Translate from 0v
if(keyboard_pitch < 0) keyboard_pitch+=12; // Deal with c being at the start, not middle of keyboard

gfxPrint(49, 55, keyboard_pitch);

//gfxBitmap(1, 55, 8, CV_ICON); gfxPos(12, 55); gfxPrintVoltage(pitches[step]);

// Debug
// gfxBitmap(1, 55, 8, CV_ICON); gfxPos(12, 55); gfxPrintVoltage(pitches[step]);


// Draw a TB-303 style octave of a piano keyboard, indicating the playing pitch
Expand Down

0 comments on commit 37a7a5b

Please sign in to comment.