Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release Starmidi: microtonal scales using lanes as "piano roll" (ALPHA) v0.1alpha #1395

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- @description Starmidi: microtonal scales using lanes as "piano roll" (ALPHA)
-- @author Starshine
-- @version 0.1alpha
-- @changelog initial alpha upload
-- @metapackage
-- @provides
-- [effect] starshine_Starmidi microtonal scales using lanes as piano roll (ALPHA)/starmidi
-- starshine_Starmidi microtonal scales using lanes as piano roll (ALPHA)/starmicro midi.eel
-- [main] starshine_Starmidi microtonal scales using lanes as piano roll (ALPHA)/scale picker lane setter.eel
-- [main] starshine_Starmidi microtonal scales using lanes as piano roll (ALPHA)/flatten.eel
-- [main] starshine_Starmidi microtonal scales using lanes as piano roll (ALPHA)/sharpen.eel
-- @about
-- These scripts and JSFX work in conjunction to do the following:
--
-- -Generate a rank-2 microtonal scale for a given track. The notes (in cents) are applied to lane names and saved to the project EXT state.
-- -Parse empty MIDI objects drawn in lanes on that track into MIDI-like data, saved in gmem
-- -Playback the notes using dynamically assigned channels and pitchbend
--
-- The following setup instructions are needed:
--
-- 1. Actions > New Action > Load ReaScript. Select "starmicro midi.eel"
-- 2. Create a custom action that runs this script, then does transport: play/stop. Assign it to your play hotkey or similar.
-- 3. Preferences > Mouse Modifiers > Track, left drag > [Default action]: "Draw an empty MIDI item". You may wish to assign this to use a key modifier to avoid disrupting your normal track+left-dag action. Empty MIDI items function as "notes" in this workflow (you will not use the regular MIDI editor ever with this)
-- 4. Actions > New Action > Load ReaScript. Select "flatten.eel", "sharpen.eel", and "scale picker lane setter.eel". Bind "flatten" and "sharpen" to hokeys of your choice, or the mouse wheel. Consider adding the scale picker script to the toolbar, or a hotkey of your choice.
-- 5. Make sure that "starmidi", the jsfx, is in the Reaper/Effects folder. (By default, I believe it will appear in a subfolder). This will allow the main on-play ReaScript to detect and automatically add it to your tracks and set its parameter appropriately. This is admittedly clunky and may be updated in the future for convenience.
-- 6. It is recommended to create actions in the toolbar or on hotkey to add/remove lanes to a track. "Track lanes: insert empty lane at top of track". Tracks must have at least 5 lanes to be parsed for playback. Each lane represents one "white key" note in the given microtonal scale. Between 10-50 lanes may be desired depending on the range of the part you are composing.
-- 7. Add a synth/sampler plugin to each track and make sure the pitch bend range is set to 2. Do not enable MPE.
-- 8. If the synth is not polyphonic, add multiple instances and set to receive MIDI on separate channels
-- 9. Write some crazy microtonal tunes!
--
-- Contact me on Discord (Starshine777) with bug reports and questions.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// @noindex

// helper script for starmidi tools
// modifies item title by adding or removing # or b symbols

GetMousePosition(x,y);
GetItemFromPoint(x,y,0,take);
GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 0);
//todo: check if item is midi

(len=strlen(#text))==0 ? #text ="b" : //case 1: empty. just add a b
match("*b*",#text) ? #text+="b" : //case 2: has one or more b. add another
match("*#*",#text) ? str_setlen(#text,len-1); //case 3: has one or more #. remove one.

GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// @noindex

//@gmem=fakemidi

MAX_TRACK_LANES = 255;
_tracks_r2_scales = 512;

ctx = ImGui_CreateContext("Rank-2 Scale Selector");
ImGui_NumericLimits_Float(FLT_MIN, FLT_MAX);
font = ImGui_CreateFont("sans_serif", 24);
ImGui_Attach(ctx, font);

color = 0;
#notes = "";
_scale_mem = 0;
SCALE_MAX_LEN = 15;
_scale_rot_mem = _scale_mem+SCALE_MAX_LEN;

function _track_scale_gmem_ptr(track)(track*MAX_TRACK_LANES+_tracks_r2_scales);

function init_defaults()(
d_steps = 7;
period = 1200;
generator = 490.0;
mode = 0;
);

function LaneNamesLetter(track,track_idx,mos_size,start_char,init_char,start_equave,period)(
track_lane_name_ptr=_track_scale_gmem_ptr(track_idx);
lane_count=GetMediaTrackInfo_Value(track, "I_NUMFIXEDLANES");
lane_idx=lane_count;
scale_degree=init_char-start_char;
equave=start_equave-1;
Loop(lane_count,
// cycle through [mos_size] consecutive letters
scale_degree%=mos_size;
scale_degree==0 ? equave+=1;
sprintf(#lane_name,"%c%i",start_char+scale_degree,equave);
scale_degree+=1;
// update the lanes starting from the bottom
sprintf(#P_LANENAME,"%s%i","P_LANENAME:",lane_idx-=1);
sprintf(#cents_and_lane,"%.2f %s",_scale_mem[scale_degree-1],#lane_name);
GetSetMediaTrackInfo_String(track, #P_LANENAME, #cents_and_lane, 1);

GetSetMediaTrackInfo_String(track, "GUID", #GUID, 0);
#section="track_lane_tuning_";
#section+=#GUID;
sprintf(#key,"%i",lane_idx);
sprintf(#value,"%f",_scale_mem[scale_degree-1]+(equave*period));
SetProjExtState(0, #section, #key, #value);
);
);

function init_scale_mem(scale)(
memset(scale,99999,SCALE_MAX_LEN);
);

function get_chroma(_mos_size,period)(
i=0; small=99999; large=0;
Loop(_mos_size,
(i==_mos_size-1)
? step=period-_scale_mem[i]
: step=_scale_mem[i+1]-_scale_mem[i]
;
step<small ? small=step;
step>large ? large=step;
i+=1;
);
large-small);

function RankTwoScale(_mos_size,_period,_generator)(
init_scale_mem(_scale_mem);
num_gens=0;
Loop(_mos_size,
scale_note=num_gens*_generator;
while(scale_note>_period)(scale_note-=_period);
num_gens+=1;
insert_pos=-1;
while(scale_note>_scale_mem[insert_pos+=1]);
mem_insert_shuffle(_scale_mem+insert_pos, SCALE_MAX_LEN-insert_pos, scale_note);
);
chroma=get_chroma(_mos_size,period);
);

function scale_mode_rotate(track,_mos_size,_period,rotate)(
i=rotate%_mos_size;
j=1;
_scale_rot_mem[0]=0;
Loop(_mos_size-1,
next_i = i+1;
next_i %= _mos_size;
_scale_rot_mem[j] = _scale_mem[next_i]
- _scale_mem[i]
+ _scale_rot_mem[j-1];
_scale_rot_mem[j] < 0 ? _scale_rot_mem[j]+=_period;
i+=1; j+=1;
i%=_mos_size;
);
memcpy(_scale_mem,_scale_rot_mem,_mos_size);
);

function print_scale_test(num_notes)(
#scale = "";
i = -1;
Loop(num_notes,
#scale+=sprintf(#,"%.3f ",_scale_mem[i+=1]);
);
);

function save_track_scale(track,track_idx,d_steps,period,generator,mode)(
GetSetMediaTrackInfo_String(track, "GUID", #GUID, 0);
#section="track_lane_tuning_";
#section+=#GUID;
sprintf(#scale_settings,"%i|%f|%f|%i|%f",d_steps,period,generator,mode,chroma);
SetProjExtState(0, #section, "vals", #scale_settings);
);

function load_track_scale(track,track_idx)(
GetSetMediaTrackInfo_String(track, "GUID", #GUID, 0);
#section="track_lane_tuning_";
#section+=#GUID;
GetProjExtState(0, #section, "vals", #scale_settings);
match("%i|%f|%f|%i|%f",#scale_settings,d_steps,period,generator,mode,chroma);
);

function frame() (
ImGui_TextWrapped(ctx, "Welcome to my scale dojo!");
ImGui_Spacing(ctx);

track = GetSelectedTrack(0, 0);
track_idx=GetMediaTrackInfo_Value(track, "IP_TRACKNUMBER")-1;

track ? (
GetTrackGUID(#guid, track);
ImGui_PushID(ctx, #guid);
) : (
ImGui_BeginDisabled(ctx);
);

!load_track_scale(track,track_idx) ? init_defaults();

ImGui_Spacing(ctx);
ImGui_SliderInt(ctx, "diatonic steps", d_steps, 1, 15);
ImGui_Spacing(ctx);
ImGui_SliderDouble(ctx, "period", period, 0, 1902, "%.3f");
ImGui_Spacing(ctx);
ImGui_SliderDouble(ctx, "generator", generator, 0, period/2, "%.3f");
ImGui_Spacing(ctx);
ImGui_SliderInt(ctx, "mode", mode, 0, d_steps-1);
ImGui_Spacing(ctx);

RankTwoScale(d_steps,period,generator);
scale_mode_rotate(track,d_steps,period,mode);
save_track_scale(track,track_idx,d_steps,period,generator,mode);
print_scale_test(d_steps);
LaneNamesLetter(track,track_idx,d_steps,'C','C',4,period);

ImGui_InputTextMultiline(ctx, "track notes", #scale, 0, -FLT_MIN);

track ? ImGui_PopID(ctx) : ImGui_EndDisabled(ctx);
);

function loop() (
open = 1;
ImGui_PushFont(ctx, font);
ImGui_SetNextWindowSize(ctx, 500, 420, ImGui_Cond_FirstUseEver());
ImGui_Begin(ctx, "Rank-2 Scale Selector", open) ? ( frame(); ImGui_PopFont(ctx); ImGui_End(ctx); );
open ? defer("loop()")
);

defer("loop()");

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @noindex

// helper script for starmidi tools
// modifies item title by adding or removing # or b symbols

GetMousePosition(x,y);
GetItemFromPoint(x,y,0,take);
GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 0);
//todo: check if item is midi

(len=strlen(#text))==0 ? #text ="#" : //case 1: empty. just add a #
match("*#*",#text) ? #text+="#" : //case 2: has one or more #. add another
match("*b*",#text) ? str_setlen(#text,len-1); //case 3: has one or more b. remove one.

GetSetMediaItemTakeInfo_String(take, "P_NAME", #text, 1);

Loading