javascripthtml

Increase or decrease notes according to music scale


When I type the music note do in the textarea and click the up button, it goes to do# and the "notes_Sharps" scale works perfectly, whether it is in uppercase or lowercase (Do or do).

What I want is:

  1. If I write reb and click the up button, to follow the "notes_Flats" scale. ['do','reb','re','mib','mi','fa','solb','sol','lab','la','sib','si'] Now, it reads the "notes_Sharps" scale.

  2. In the same way, I want exactly the same thing if I write ρε# or ρεb, to follow the corresponding scales, "notes_Sharps_Gr" and "notes_Flats_Gr"

How do I program it? It's been a week and I'm still at this result.. Thanks in advance

const
  textareaId = document.querySelector('#textarea'),
  notes_Sharps = ['do', 'do#', 're', 're#', 'mi', 'fa', 'fa#', 'sol', 'sol#', 'la', 'la#', 'si'],

  notes_Flats = ['do', 'reb', 're', 'mib', 'mi', 'fa', 'solb', 'sol', 'lab', 'la', 'sib', 'si'],
  notes_Sharps_Gr = ['ντο', 'ντο#', 'ρε', 'ρε#', 'μι', 'φα', 'φα#', 'σολ', 'σολ#', 'λα', 'λα#', 'σι'],
  notes_Flats_Gr = ['ντο', 'ρεb', 'ρε', 'μιb', 'μι', 'φα', 'σολb', 'σολ', 'λαb', 'λα', 'σιb', 'σι'],

  notesExchg = {
    reb: 'do#',
    mib: 're#',
    solb: 'fa#',
    lab: 'sol#',
    sib: 'la#'
  },


  changeNotes = val => notesExchg[val.toLowerCase()] || val;

function stepOn(stepNotes = +1) {
  let textarea_regex = textareaId.value.split(/([A-Za-zÉé]+\#?)/);

  textareaId.value = textarea_regex.reduce((acc, Nx) => {
    if (!Boolean(Nx))
      return acc;

    let flatToSharp = changeNotes(Nx),
      isUpC = Nx[0].toUpperCase() === Nx[0],
      noteSearch = notes_Sharps.findIndex(x => flatToSharp.toLowerCase() === x);

    if (noteSearch === -1)
      acc += Nx;

    else {
      let NotesStep = notes_Sharps[(noteSearch + stepNotes + notes_Sharps.length) % notes_Sharps.length];
      acc += isUpC ? NotesStep.toUpperCase() : NotesStep;
    }
    return acc;
  }, '')
}
<button onclick="stepOn(+1 )" class="stepUp_Button">Up</button>
<button onclick="stepOn(-1 )" class="stepDown_Button">Down</button>


<textarea id="textarea" rows="5" cols="30">
</textarea>
</div>


Solution

  • Try this solution based on your solution. It saves the cache and remembers the each individual notes. Comments are in the code (shared notations like do will be treated as sharp notation).

    const
      textareaId = document.querySelector('#textarea'),
      notes_Sharps = ['do', 'do#', 're', 're#', 'mi', 'fa', 'fa#', 'sol', 'sol#', 'la', 'la#', 'si'],
      notes_Flats = ['do', 'reb', 're', 'mib', 'mi', 'fa', 'solb', 'sol', 'lab', 'la', 'sib', 'si'],
      notes_Sharps_Gr = ['ντο', 'ντο#', 'ρε', 'ρε#', 'μι', 'φα', 'φα#', 'σολ', 'σολ#', 'λα', 'λα#', 'σι'],
      notes_Flats_Gr = ['ντο', 'ρεb', 'ρε', 'μιb', 'μι', 'φα', 'σολb', 'σολ', 'λαb', 'λα', 'σιb', 'σι'];
    
    const cachedScales = [];
    
    function stepOn(stepNotes = +1) {
      let textarea_regex = textareaId.value.split(/([A-Za-zÉéΑ-Ωα-ω]+#?)/);
      
      textareaId.value = textarea_regex.reduce((acc, Nx, i) => {
        if (!Nx) return acc;
        
        // find the target scale for each notes from the cache, if not found, search through all the notes list
        const targetScale = cachedScales[i] ?? [notes_Sharps, notes_Flats, notes_Sharps_Gr, notes_Flats_Gr].find(scales => scales.includes(Nx.toLowerCase()));
        const noteSearch = targetScale?.indexOf(Nx.toLowerCase()) ?? -1;
        
        if (noteSearch === -1) {
          acc += Nx;
        } else {
          cachedScales[i] = targetScale;  // save the notes to cache
          const isUpC = Nx[0].toUpperCase() === Nx[0];
          const isCap = isUpC && Nx[1] && Nx[1].toUpperCase() !== Nx[1];
          const NotesStep = targetScale[(noteSearch + stepNotes + targetScale.length) % targetScale.length];
          acc += (isCap ? NotesStep.charAt(0).toUpperCase() + NotesStep.substring(1) : isUpC ? NotesStep.toUpperCase() : NotesStep).replace(/(?<=.)B$/, 'b');
        }
        return acc;
      }, '')
    }
    
    // upon editing, clear the cache
    textareaId.addEventListener('input', () => cachedScales.length = 0);
    <button onclick="stepOn(+1 )" class="stepUp_Button">Up</button>
    <button onclick="stepOn(-1 )" class="stepDown_Button">Down</button>
    
    
    <textarea id="textarea" rows="5" cols="30">
    </textarea>