adielBm / chord-extractor

extract chords from an audio file (using ohollo/chord-extractor & Chordino)
4 stars 1 forks source link

Sustained chord diagrams bug #1

Closed alexandrglm closed 1 month ago

alexandrglm commented 1 month ago

It seems that the chord-extractor output for sharp # chords envisages this kind of chords as the URL encoded (for unsafe ASCII) %23.

Seems to be a good way to mention them, but then, many browsers aren't allowed of loading localfiles with any URL enconded characters in its filename, so that adding alternative ways to rename any sharps chord its required in order to get a fully working chord diagrams.

Example: 1. PARSING/TRANSLATING SHARP CHORDS to their FLAT version: If we change "F#" to "Gb", So... F%23.png would be renamed as Gb.png

A simple parser/translator should be added in chord-extractor to provide a fully working chord diagrams, in consequence.

Thanks.

alexandrglm commented 1 month ago

When debugging the scripts, script.js seems to be involved in generating the html output, specifically the simplifyChord and encodeURIComponent functions.

It is proposed as a solution to recode SHARP # to FLAT ♭ before being processed by encodeURIComponent.

alexandrglm commented 1 month ago

script.js code proposal to solve noticed issue:

document.addEventListener("DOMContentLoaded", function () {
  const audio = document.querySelector("audio");
  const chords = document.querySelectorAll("#chords li");
  const transposeCounter = document.getElementById("transpose-counter");
  const transposeUpButton = document.getElementById("transpose-up");
  const transposeDownButton = document.getElementById("transpose-down");
  const capoCounter = document.getElementById("capo-counter");
  const chordDiagramCurrent = document.getElementById("chord-diagram-current");
  const chordDiagramNext = document.getElementById("chord-diagram-next");
  const chordCurrent = document.getElementById("chord-current");
  const chordNext = document.getElementById("chord-next");

  let maxChordLength = 0;
  let instrument = "guitar";

  chords.forEach((chord) => {
    chord.addEventListener("click", function () {
      audio.currentTime = chord.id;
      audio.play();
    });
  });

  document.querySelectorAll('input[name="instrument"]').forEach((input) => {
    input.addEventListener("change", function () {
      instrument = this.value;
    });
  });

  chords.forEach((chord, index) => {
    var nextChord = chords[index + 1];
    if (nextChord) {
      var length = nextChord.id - chord.id;
      chord.setAttribute("length", length);
      if (length > maxChordLength) {
        maxChordLength = length;
      }
    }
  });

  chords.forEach((chord) => {
    let length = chord.getAttribute("length");
    const n = 3;
    length = parseFloat(length).toFixed(n);
    let w = (length / maxChordLength.toFixed(n)) * 400;
    chord.style.width = w + "px";
  });

  chords.forEach((chord) => {
    let length = chord.getAttribute("length");
    chord.style.setProperty("--animation-duration", length + "s");
  });

  function scaleChordWidth(x) {
    chords.forEach((chord) => {
      chord.style.width = chord.style.width.replace("px", "") * x + "px";
    });
  }

  document.getElementById("zoom-in").addEventListener("click", function () {
    scaleChordWidth(1.5);
  });

  document.getElementById("zoom-out").addEventListener("click", function () {
    scaleChordWidth(0.8);
  });

  transposeUpButton.addEventListener("click", function () {
    transposeChords(1);
    transposeCounter.innerHTML = parseInt(transposeCounter.innerHTML) + 1;
    capoCounter.innerHTML = transposeCounter.innerHTML * -1;
  });

  transposeDownButton.addEventListener("click", function () {
    transposeChords(-1);
    transposeCounter.innerHTML = parseInt(transposeCounter.innerHTML) - 1;
    capoCounter.innerHTML = transposeCounter.innerHTML * -1;
  });

  setInterval(function () {
    const currentTime = audio.currentTime;
    chords.forEach((chord) => {
      if (chord.id <= currentTime) {
        chord.classList.add("actived");
      } else {
        chord.classList.remove("actived");
        chord.classList.remove("active");
      }

      if (chord.id - currentTime <= 0.3 && chord.id - currentTime >= 0) {
        chord.classList.add("active");

        if (chordCurrent.innerHTML != chord.innerHTML) {
          chordCurrent.innerHTML = chord.innerHTML;
          chordDiagramCurrent.src = `${instrument}/${simplifyChord(chord.innerHTML)}.png`;
        }

        if (chordNext.innerHTML != chord.nextElementSibling.innerHTML) {
          chordNext.innerHTML = chord.nextElementSibling.innerHTML;
          chordDiagramNext.src = `${instrument}/${simplifyChord(chord.nextElementSibling.innerHTML)}.png`;
        }
      } else {
      }
    });
  }, 150);

  function transposeChords(amount) {
    chords.forEach((chord) => {
      chord.innerHTML = transposeChord(chord.innerHTML, amount);
    });
  }

  function transposeChord(chord, amount) {
    var scale = [
      "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
    ];
    var normalizeMap = {
      Cb: "B", Db: "C#", Eb: "D#", Fb: "E", Gb: "F#", Ab: "G#", Bb: "A#", "E#": "F", "B#": "C"
    };
    return chord.replace(/[CDEFGAB](b|#)?/g, function (match) {
      var i = (scale.indexOf(normalizeMap[match] ? normalizeMap[match] : match) + amount) % scale.length;
      return scale[i < 0 ? i + scale.length : i];
    });
  }

  function simplifyChord(chord) {
    const chordMap = {
      "C#": "Db", "C%23": "Db",
      "D#": "Eb", "D%23": "Eb",
      "F#": "Gb", "F%23": "Gb",
      "G#": "Ab", "G%23": "Ab",
      "A#": "Bb", "A%23": "Bb",
    };

    return chord
      .replace(/\/.*/, "")
      .replace(/C%23|C#/g, "Db")
      .replace(/D%23|D#/g, "Eb")
      .replace(/F%23|F#/g, "Gb")
      .replace(/G%23|G#/g, "Ab")
      .replace(/A%23|A#/g, "Bb");
  }
});
adielBm commented 1 month ago

many browsers aren't allowed of loading localfiles with any URL enconded characters in its filename,

interesting. i didn't know that.

script.js code proposal to solve noticed issue:

very nice. but it has to be also for minor and 7 chords .. etc.. kind of complicated. but i think there are js libraries can do it.

alexandrglm commented 1 month ago

Since sharp chords, their notation in filename, start with "X%23", it should work for those more complex chords (In the end, what I'm asking it to "translate" are the filenames.png it will use).

However, I'd have time to do a good test of each chord type and how it is converted. Therefore, I've created a fork, because I'll be making deep changes to adapt it to what I tend to use, for example, Chordify service.

For example, I'm probably going to omit conversions to Ukulele (or not complete them), I'd add a way to show variations of each specific chord, if I have time I would also add a way to indicate the type of tuning I am using on the instrument, I'd modify the way the html output is generated, and probably some more.

alexandrglm commented 1 month ago

All chords are functional with the proposed changes. I'm closing the issue, because I don't know what you'll do with your code.

I've forked it, I'm implementing other functionalities, although the base is your main here. I quote you in the readme.

By the way, the reason why encodedURI is used comes from chordino, it's its "way" of passing a chord to text. Regards. Alexandr.

adielBm commented 1 month ago

good luck!

by the way, I’ve just pushed some updates, including adding yt-dlp. like this:

like add using in yt-dlp like C:\...\chord-extractor> python38 "cho.py" "https://music.youtube.com/watch?v=Bb5xJN_4Hck&si=AIwCDplhOlWGgCim"

alexandrglm commented 1 month ago

Oh, sounds so nice, dude. I'm focusing my idea as an "offline" tool so... I won't include "youtube fetching".

On the other hand, I'm starting from the scratch, making deep deep changes so, At the rate it's going, it will no longer be a fork from your work, but I will mention about you in license.md.