StemTransposition

Super super random and out there request…

I’ve set up a page on my canvas with a bunch of buttons that control a Waves soundshifter plugin on each of my return tracks for my stems. Basically each button sets the plugin to pitch shift +1, +2, -1, -2, etc…
I’ve setup each of my songs so the key of the song is in the song description.
What I’d ideally like is to be able to have a display in my transposition page of what the original key of the song is, and then what the current key is (whatever I may have modified with my buttons)

I was wondering if there was a way for Ableset to read the key of the song from the song description?

The process in my head would look like this…

  • Song key is in song description
  • When clicking one of the transpose buttons, Ableset reads the current key of the song from the song description (maybe has to convert to some kind of logic or numbers?)
  • Ableset then changes the “key” accordingly to however much it has been pitch-shifted by

I’m not sure really sure how this would work or if it’s possible currently, I’m assuming using some kind of variables it might be able to happen. Like if every key was converted to an array of numbers for example
C = 1
Db = 2
D = 3
etc….

And then when I click “+1” button, it adds 1 to the existing variable, changing C to Db….

That may not make any sense, sorry if it doesn’t :sweat_smile:

Hey @Adam_Molinaro, that’s a cool idea!

You should be able to make this work using a map and an array. First, you can define a map that assigns a number to each note on the chromatic scale:

const notes = { "C": 0, "C#": 1, "Db": 1, "D": 2, "D#": 3, "Eb": 3, "E": 4, "F": 5, "F#": 6, "Gb": 6, "G": 7, "G#": 8, "Ab": 8, "A": 9, "A#": 10, "Bb": 10, "B": 11 };

Next, you can define an array of target notes:

const targets = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];

You can then use the activeSongDescription OSC value to get the description of the current song and map it to the number:

// This would be 0 for "C", 1 for "C#", etc.
const description = osc("/setlist/activeSongDescription");
const originalKey = notes[description];

To get the transposed key, we can add the transposition to the originalKey variable:

// Make sure the transpose is always higher than 0, 
// even when transposing down, by adding two octaves
const transpose = Number(shared("transpose") ?? 0) + 24;
const tranposedKey = targets[(originalKey + transpose) % targets.length];

Putting it all together, the template for the label could look like this:

${(() => {
  const notes = { "C": 0, "C#": 1, "Db": 1, "D": 2, "D#": 3, "Eb": 3, "E": 4, "F": 5, "F#": 6, "Gb": 6, "G": 7, "G#": 8, "Ab": 8, "A": 9, "A#": 10, "Bb": 10, "B": 11 };
  const targets = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];

  // This would be 0 for "C", 1 for "C#", etc.
  const description = osc("/setlist/activeSongDescription");
  const originalKey = notes[description];
  
  // Make sure the transpose is always higher than 0, 
  // even when transposing down, by adding two octaves
  const transpose = Number(shared("transpose") ?? 0) + 24;

  // If the description doesn't match any key, return it as-is
  if (originalKey === undefined || transpose === 0) {
    return description;
  }
  
  // The % is used to wrap numbers back to 0 if they 
  // exceed the length of the targets array, e.g. 14 -> 2
  return targets[(originalKey + transpose) % targets.length];
})()}

Let me know if this works for you :slight_smile:

1 Like

Hi Leo, thanks for this! I’ve got it working!

I had a couple more questions…

1 - Is there a way to make the transposition save value per song? That way whenever I recall a song it’ll keep it’s last saved transposition value
Basically my idea here is I want to transpose purely from Ableset instead of going to each stem and doing it manually. The benefit of doing it manually though, is that it is per song.

2 - If this “per song” transposition is doable, then how would I go about displaying EACH song’s current key within a song button? The song buttons also use variables to have “pages”. So they would have to be able to respect whatever “page” it’s on as well.

I know this is probably getting a bit complex, thank you for all the time you spend helping us out :smiley: