Old 11-14-2019, 06:13 PM   #1
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 1,134
Default Sort Selected Notes Table Low to High

I got my selected notes (48 to 106) in a table and I need to sort them from lowest note 43 idx 73 "43,73" to highest note 106 idx 94 "106,94"
I tried table.sort

Code:
return {
-- Table: {1}
{
   "103,76",
   "104,82",
   "105,88",
   "106,94",
   "43,73",
   "48,77",
   "48,79",
   "48,83",
   "48,85",
   "48,89",
   "48,91",
   "48,95",
   "55,75",
   "55,78",
   "55,84",
   "55,87",
   "55,90",
   "55,96",
   "57,81",
   "57,93",
   "96,74",
   "97,80",
   "98,86",
   "99,92",
},
}
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
MusoBob is offline   Reply With Quote
Old 11-14-2019, 08:21 PM   #2
Edgemeal
Human being with feelings
 
Edgemeal's Avatar
 
Join Date: Apr 2016
Location: ASU`ogacihC
Posts: 1,482
Default

If you have two or more values per item it might be easier to store the data in separate properties, something like...

Code:
local notes = {}

-- add test data to table
notes[1] = { value=10, velocity=64 }
notes[2] = { value=106, velocity=127 }
notes[3] = { value=9, velocity=100 

-- sort notes table by 'value' property ascending
table.sort(notes, function(a,b) return a.value < b.value end) 

-- sort by 'velocity' descending
table.sort(notes, function(a,b) return a.velocity > b.velocity end)

Last edited by Edgemeal; 11-14-2019 at 08:37 PM.
Edgemeal is offline   Reply With Quote
Old 11-15-2019, 02:00 AM   #3
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 1,134
Default

Thanks that worked to get the notes in ascending order.

Code:
return {
-- Table: {1}
{
   {2},
   {3},
   {4},
   {5},
   {6},
   {7},
   {8},
   {9},
   {10},
   {11},
   {12},
   {13},
   {14},
   {15},
   {16},
   {17},
   {18},
   {19},
   {20},
   {21},
   {22},
   {23},
   {24},
   {25},
},
-- Table: {2}
{
   ["sort_idx"]=73,
   ["sort_pitch"]=43,
},
-- Table: {3}
{
   ["sort_idx"]=79,
   ["sort_pitch"]=48,
},
-- Table: {4}
{
   ["sort_idx"]=85,
   ["sort_pitch"]=48,
},
-- Table: {5}
{
   ["sort_idx"]=95,
   ["sort_pitch"]=48,
},
-- Table: {6}
{
   ["sort_idx"]=77,
   ["sort_pitch"]=48,
},
-- Table: {7}
{
   ["sort_idx"]=83,
   ["sort_pitch"]=48,
},
-- Table: {8}
{
   ["sort_idx"]=89,
   ["sort_pitch"]=48,
},
-- Table: {9}
{
   ["sort_idx"]=91,
   ["sort_pitch"]=48,
},
-- Table: {10}
{
   ["sort_idx"]=90,
   ["sort_pitch"]=55,
},
-- Table: {11}
{
   ["sort_idx"]=87,
   ["sort_pitch"]=55,
},
-- Table: {12}
{
   ["sort_idx"]=84,
   ["sort_pitch"]=55,
},
-- Table: {13}
{
   ["sort_idx"]=96,
   ["sort_pitch"]=55,
},
-- Table: {14}
{
   ["sort_idx"]=78,
   ["sort_pitch"]=55,
},
-- Table: {15}
{
   ["sort_idx"]=75,
   ["sort_pitch"]=55,
},
-- Table: {16}
{
   ["sort_idx"]=81,
   ["sort_pitch"]=57,
},
-- Table: {17}
{
   ["sort_idx"]=93,
   ["sort_pitch"]=57,
},
-- Table: {18}
{
   ["sort_idx"]=74,
   ["sort_pitch"]=96,
},
-- Table: {19}
{
   ["sort_idx"]=80,
   ["sort_pitch"]=97,
},
-- Table: {20}
{
   ["sort_idx"]=86,
   ["sort_pitch"]=98,
},
-- Table: {21}
{
   ["sort_idx"]=92,
   ["sort_pitch"]=99,
},
-- Table: {22}
{
   ["sort_idx"]=76,
   ["sort_pitch"]=103,
},
-- Table: {23}
{
   ["sort_idx"]=82,
   ["sort_pitch"]=104,
},
-- Table: {24}
{
   ["sort_idx"]=88,
   ["sort_pitch"]=105,
},
-- Table: {25}
{
   ["sort_idx"]=94,
   ["sort_pitch"]=106,
},
}
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
MusoBob is offline   Reply With Quote
Old 11-16-2019, 12:37 AM   #4
-_~
Human being with feelings
 
Join Date: Oct 2019
Posts: 19
Default

Just for completeness, the reason the initial sort did not work is because it was using the value of the first, then second etc characters in the strings. There needs to be a conversion to numbers for it to work:
Code:
table.sort(t,
  function(note_vel1, note_vel2)
    -- pattern used with match returns () two substrings that match
    -- one or more (+) digits (%d) each, seperated by a comma
    local pattern = "(%d+),(%d+)"
    local note1, vel1 = note_vel1:match(pattern)
    local note2, vel2 = note_vel2:match(pattern)
    -- they are still strings at this point, so convert to compare
    return tonumber(note1) < tonumber(note2)
  end
)

-- or on one line
table.sort(t, function(a, b) return tonumber(a:match("(%d+)")) < tonumber(b:match("(%d+)")) end)
But all of that pattern matching is pretty slow, so it's best to avoid doing this and to get the right data format for the job.
-_~ is offline   Reply With Quote
Old 11-16-2019, 04:55 AM   #5
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 1,134
Default

This is what I have:

Get selected notes
Code:
sel_notes = {}
for i = 0, notecnt -1 do
  _, selected, muted, startppqpos, endppqpos, chan, pitch, vel = reaper.MIDI_GetNote( take, i )
  if selected then
    sel_notes[startppqpos] = {Start = startppqpos, End = endppqpos, Pitch = pitch, Idx = i}
    sel_notes[endppqpos] = {Start = startppqpos, End = endppqpos, Pitch = pitch, Idx = i}
    sel_notes[pitch] = {Start = startppqpos, End = endppqpos, Pitch = pitch, Idx = i}
    sel_notes[i] = {Start = startppqpos, End = endppqpos, Pitch = pitch, Idx = i}
end
Found Selected note 52 @ 55
Found Selected note 58 @ 56
Found Selected note 63 @ 57
Found Selected note 58 @ 58
Found Selected note 63 @ 59
Found Selected note 52 @ 60
Found Selected note 52 @ 61
Found Selected note 63 @ 62
Found Selected note 58 @ 63
Found Selected note 63 @ 64
Found Selected note 52 @ 65
Found Selected note 58 @ 66
Found Selected note 52 @ 67
Found Selected note 63 @ 68
Found Selected note 58 @ 69

Create new table for pitch and idx starting from 1
Code:
selected_pitch_order = {}
order = 0
for i = first_sel_note_index, first_note_index + sel_notes_count do   
  order = order +1
  if order > count then break end
  pitch =sel_notes[i].Pitch
  id = sel_notes[i].Idx
  selected_pitch_order[order] = { sort_pitch = pitch, sort_idx = id }
end
Sort pitch ascending
Code:
table.sort(selected_pitch_order, function(a,b) return a.sort_pitch < b.sort_pitch end)
Sorted note table
i 1
pitch 52 idx 55
i 2
pitch 52 idx 65
i 3
pitch 52 idx 61
i 4
pitch 52 idx 60
i 5
pitch 52 idx 67
i 6
pitch 58 idx 66
i 7
pitch 58 idx 69
i 8
pitch 58 idx 69
i 9
pitch 58 idx 56
i 10
pitch 63 idx 57
i 11
pitch 63 idx 59
i 12
pitch 63 idx 68
i 13
pitch 63 idx 68
i 14
pitch 63 idx 64
i 15
pitch 63 idx 62

The issue I have is the in the original Found Selected notes
there are
5 x note 52
5 x note 58
5 x note 63
but in the new sorted table
5 x note 52
4 x note 58
6 x note 63

I have missed something, maybe there's a better way to do it ?
I need them in order to match to another table that has all the notes for that chord.
So it moves all the lowest notes to the nearest root then the next pitch notes to the next chord note and so on.
Also when I select the first lot of notes it misses the first note that is idx 0

Chord notes table (Gmaj)
Code:
return {
-- Table: {1}
{
   2,
   7,
   11,
   14,
   19,
   23,
   26,
   31,
   35,
   38,
   43,
   47,
   50,
   55,
   59,
   62,
   67,
   71,
   74,
   79,
   83,
   86,
   91,
   95,
   98,
   103,
   107,
   110,
   115,
   119,
   122,
   127,
},
}
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
MusoBob is offline   Reply With Quote
Old 11-17-2019, 03:00 AM   #6
-_~
Human being with feelings
 
Join Date: Oct 2019
Posts: 19
Default

Here's a function to get the selected notes and sort them (there's no need for a new table):
Code:
local function getSelectedNotesFromTake(take, sort_by_pitch)
  local sel_notes = {}
  local idx = 0
  while (true) do
    local ok, selected, muted, startppq, endppq, chan, pitch, vel = reaper.MIDI_GetNote(take, idx)
    if not ok then break end
    if selected then
      sel_notes[#sel_notes + 1] = {
        idx = idx, startppq = startppq, endppq = endppq,
        chan = chan, pitch = pitch, vel = vel
      }
    end
    idx = idx + 1
  end
  if sort_by_pitch then
    table.sort(sel_notes, function(a, b) return a.pitch < b.pitch end)
  end
  return sel_notes
end
If you look at the original, there are 4 writes to the same array per note but using 4 different values to index. That's going to produce wonky results, especially as the main one (storing notes sequentially) is actually skipping entries if notes are not selected.

I personally wouldn't use tables for individual chords like that either since everything is relative, eg a D chord is a C chord + 2 for every note, and every octave is the same. I'd go for something like this:

Code:
local Chords = {}
Chords.notes = {
  ["C"] = 0, ["C#"] = 1, ["Db"] = 1, ["D"] = 2,   ["D#"] = 3, ["Eb"] = 3,
  ["E"] = 4, ["E#"] = 5, ["Eb"] = 5, ["F"] = 6,   ["F#"] = 7, ["Gb"] = 7,
  ["G"] = 8, ["G#"] = 9, ["A"] = 10, ["A#"] = 11, ["Bb"] = 11
}

Chords.major = {
  [0] = 0, -- root note
  [1] = -1,
  [2] = -2,
  [3] = 1,
  [4] = 0,
  [5] = -1,
  [6] = 1,
  [7] = 0,
  [8] = -1,
  [9] = -2,
  [10] = 2,
  [11] = 1
}

Chords.noteToChord = function(note, scale_root, chord_type)
  if type(scale_root) == "string" then scale_root = Chords.notes[scale_root] end
  return note + Chords[chord_type][(note - scale_root) % 12]
end
---------------------------------------------------------------

-- it doesn't matter what octave the note you pass in is on
local note = 58
note = Chords.noteToChord(note, "D", "major")

-- can pass numeric scale root (D == 2) too
local same_note = Chords.noteToChord(note, 2, "major")
... where you'd just need to add one octave of note adjustments per scale type.
-_~ is offline   Reply With Quote
Old 11-17-2019, 03:14 AM   #7
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 1,134
Default

Thanks that's a breath of fresh air as I got so bogged down in it.
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
MusoBob is offline   Reply With Quote
Old 11-17-2019, 04:01 AM   #8
-_~
Human being with feelings
 
Join Date: Oct 2019
Posts: 19
Default

No worries, it's good to get another set of eyes/ears onto things at times.
-_~ is offline   Reply With Quote
Old 11-17-2019, 04:38 PM   #9
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 1,134
Default

I'm not getting a sort by pitch with that.
How do I message it out as it gives a nil value field
Msg("pitch "..sel_notes[116].pitch)

Code:
return {
-- Table: {1}
{
   {2},
   {3},
   {4},
   {5},
   {6},
   {7},
   {8},
   {9},
   {10},
   {11},
   {12},
   {13},
   {14},
   {15},
   {16},
},
-- Table: {2}
{
   ["startppq"]=27840.0,
   ["vel"]=71,
   ["chan"]=12,
   ["pitch"]=52,
   ["endppq"]=28790.0,
   ["idx"]=116,
},
-- Table: {3}
{
   ["startppq"]=27840.0,
   ["vel"]=71,
   ["chan"]=12,
   ["pitch"]=58,
   ["endppq"]=28720.0,
   ["idx"]=117,
},
-- Table: {4}
{
   ["startppq"]=27840.0,
   ["vel"]=71,
   ["chan"]=12,
   ["pitch"]=63,
   ["endppq"]=28760.0,
   ["idx"]=118,
},
-- Table: {5}
{
   ["startppq"]=28800.0,
   ["vel"]=70,
   ["chan"]=12,
   ["pitch"]=58,
   ["endppq"]=28850.0,
   ["idx"]=119,
},
-- Table: {6}
{
   ["startppq"]=28800.0,
   ["vel"]=105,
   ["chan"]=12,
   ["pitch"]=63,
   ["endppq"]=28860.0,
   ["idx"]=120,
},
-- Table: {7}
{
   ["startppq"]=28800.0,
   ["vel"]=75,
   ["chan"]=12,
   ["pitch"]=52,
   ["endppq"]=28820.0,
   ["idx"]=121,
},
-- Table: {8}
{
   ["startppq"]=29760.0,
   ["vel"]=75,
   ["chan"]=12,
   ["pitch"]=52,
   ["endppq"]=30230.0,
   ["idx"]=122,
},
-- Table: {9}
{
   ["startppq"]=29760.0,
   ["vel"]=71,
   ["chan"]=12,
   ["pitch"]=63,
   ["endppq"]=30230.0,
   ["idx"]=123,
},
-- Table: {10}
{
   ["startppq"]=29760.0,
   ["vel"]=71,
   ["chan"]=12,
   ["pitch"]=58,
   ["endppq"]=30190.0,
   ["idx"]=124,
},
-- Table: {11}
{
   ["startppq"]=30230.0,
   ["vel"]=74,
   ["chan"]=12,
   ["pitch"]=63,
   ["endppq"]=30270.0,
   ["idx"]=125,
},
-- Table: {12}
{
   ["startppq"]=30240.0,
   ["vel"]=62,
   ["chan"]=12,
   ["pitch"]=52,
   ["endppq"]=30280.0,
   ["idx"]=126,
},
-- Table: {13}
{
   ["startppq"]=30240.0,
   ["vel"]=70,
   ["chan"]=12,
   ["pitch"]=58,
   ["endppq"]=30280.0,
   ["idx"]=127,
},
-- Table: {14}
{
   ["startppq"]=31200.0,
   ["vel"]=83,
   ["chan"]=12,
   ["pitch"]=52,
   ["endppq"]=31680.0,
   ["idx"]=128,
},
-- Table: {15}
{
   ["startppq"]=31200.0,
   ["vel"]=73,
   ["chan"]=12,
   ["pitch"]=63,
   ["endppq"]=31680.0,
   ["idx"]=129,
},
-- Table: {16}
{
   ["startppq"]=31210.0,
   ["vel"]=73,
   ["chan"]=12,
   ["pitch"]=58,
   ["endppq"]=31680.0,
   ["idx"]=130,
},
}

if I use
Code:
table.insert(sel_notes, "Pitch="..pitch .. " Idx="..i)
then use the sort
Code:
table.sort(sel_notes, function(a,b) return a < b end)
Code:
pattern = "Pitch=(%d+) Idx=(%d+)" 
pitch, index = sel_notes[i]:match(pattern)

Code:
return {
-- Table: {1}
{
   "Pitch=52 Idx=131",
   "Pitch=52 Idx=136",
   "Pitch=52 Idx=137",
   "Pitch=52 Idx=141",
   "Pitch=52 Idx=143",
   "Pitch=58 Idx=132",
   "Pitch=58 Idx=134",
   "Pitch=58 Idx=139",
   "Pitch=58 Idx=142",
   "Pitch=58 Idx=145",
   "Pitch=63 Idx=133",
   "Pitch=63 Idx=135",
   "Pitch=63 Idx=138",
   "Pitch=63 Idx=140",
   "Pitch=63 Idx=144",
},
}
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
MusoBob is offline   Reply With Quote
Old 11-17-2019, 05:23 PM   #10
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 1,134
Default

But now I find I get this:
Code:
return {
-- Table: {1}
{
   "Pitch=103 Idx=92",
   "Pitch=107 Idx=76",
   "Pitch=110 Idx=82",
   "Pitch=115 Idx=88",
   "Pitch=119 Idx=94",
   "Pitch=43 Idx=73",
   "Pitch=43 Idx=77",
   "Pitch=43 Idx=79",
   "Pitch=43 Idx=83",
   "Pitch=43 Idx=85",
   "Pitch=43 Idx=89",
   "Pitch=43 Idx=91",
   "Pitch=43 Idx=95",
   "Pitch=47 Idx=75",
   "Pitch=47 Idx=78",
   "Pitch=47 Idx=84",
   "Pitch=47 Idx=87",
   "Pitch=47 Idx=90",
   "Pitch=47 Idx=96",
   "Pitch=50 Idx=81",
   "Pitch=50 Idx=93",
   "Pitch=91 Idx=74",
   "Pitch=95 Idx=80",
   "Pitch=98 Idx=86",
},
}
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
MusoBob is offline   Reply With Quote
Old 11-18-2019, 01:07 AM   #11
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 1,134
Default

This all seems to work now as rough as it is.

ReaTrak snap midi takes in selection to chords.lua

so whatever the region chord name is it will snap any midi
starting in the region time selection to the chord notes,
snapping the lowest note to the closest chord root,
the next pitch up to the next chord note and so on.
It will do the same to any notes in the above octaves ( any notes > root +23).
It will do one region that is selected or all regions in the time selection.
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
MusoBob is offline   Reply With Quote
Old 11-18-2019, 03:25 AM   #12
-_~
Human being with feelings
 
Join Date: Oct 2019
Posts: 19
Default

Cool, good stuff getting it working!

Remember you have all the data you need in these table entries:
Code:
{
   ["startppq"]=27840.0,
   ["vel"]=71,
   ["chan"]=12,
   ["pitch"]=52,
   ["endppq"]=28790.0,
   ["idx"]=116,
}
But you have created additional text entries and then gone back to sorting those strings alphabetically, where "1000000000" is always going to be 'smaller' than "2".

You can instead just sort that table:
Code:
table.sort(t, function(a, b) a.pitch < b.pitch end)
... if you want to sort by pitch, then idx:
Code:
table.sort(t, function(a, b)
    if a.pitch ~= b.pitch then
      return a.pitch < b.pitch
    end
    return a.idx < b.idx
  end
)
The nil error message you get:
Code:
Msg("pitch "..sel_notes[116].pitch)
... is because you are trying to get the pitch field of the 116th entry in the sel_notes table, but this new method doesn't use non-sequential note idxs (leaving nil entries/gaps) that don't start at 1 (or zero) as indexes anymore, which is generally a bad idea because using different methods to iterate produce different results.
-_~ is offline   Reply With Quote
Old 11-18-2019, 03:54 AM   #13
MusoBob
Human being with feelings
 
MusoBob's Avatar
 
Join Date: Sep 2014
Posts: 1,134
Default

Thanks I appreciate your help. your way should simplify things,
I'll get back in the ring and take another swing after I've had a rest from it, I think it's
ReaTrak snap midi takes in selection to chords - Copy (16).lua
__________________
ReaTrakStudio Chord Track for Reaper forum
www.reatrak.com
MusoBob is offline   Reply With Quote
Old 11-18-2019, 06:39 AM   #14
-_~
Human being with feelings
 
Join Date: Oct 2019
Posts: 19
Default

We've all been there, at least 16 times!
-_~ is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 12:11 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.