Linuchs: Taktnummern markieren zum AUDIO-Tag (Chor)

Moin,

für Chor-Stimmen biete ich fünf Audio-Tracks auf einer Webseite, die synchron ablaufen. Sinn der Übung: Jeder kann zuhause üben und seine Stimme lauter stellen.

Very nice to have: Hinter den Audio-Spuren senkrechte Markierungen für die Taktnummern der Einsätze.

Mein Stück hat 127 Takte, jeder Takt belegt horizontal also calc( 100% / 127 ) Prozent. Wenn der weiße Fortschrittskreis bei Takt 19 ist, soll er bei der senkrechten Linie 19 sein.

Mein Muster wäre für width des Audio-Tracks richtig, aber nicht für die Laufspur (dunkelrot):

Frage: Wie ermittle ich left und width der Laufspur? Bzw. kann ich die Laufspur auslagern, sodass ich eine definierte width habe?

Gruß, Linuchs

  1. Hallo Linuchs,

    das Ermitteln der Laufspur stelle ich mir schwierig vor. Es gibt CSS Pseudoklassen, mit denen Du die Laufspur und andere Control-Elemente stylen kannst, aber die sind nicht einheitlich (bspw. ::-webkit-media-controls-timeline - guckst Du).

    Je nach Browser dürfte das Layout der Controls auch unterschiedlich sein, so dass Dir dafür Probleme mit der Portabilität drohen könnten.

    Ich würde daher eher das currentTime Property des HTMLAudioElements verwenden und damit selbst eine Laufspur darstellen. Dann hast Du die Takt-Einbettung selbst in der Hand.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      Ich würde daher eher das currentTime Property des HTMLAudioElements verwenden und damit selbst eine Laufspur darstellen. Dann hast Du die Takt-Einbettung selbst in der Hand.

      danke dir. Ja, das habe ich auch. Mehrere Takte zusammengefasst, das was läuft, ist gelb hinterlegt:

      Muss mal überlegen, ob die Darstellung von 127 Takten noch übersichtlich ist ...

      PS: Wer übt, muss nicht immer bei Takt 1 anfangen, er kann auf die entspr. Textzeile klicken und die Audio-Spuren beginnen dort.

      Gruß, Linuchs

      1. Hallo,

        der Abschnitt für Alt/Tenor ist einmal 11 Takte lang (gelb markiert), dann zweimal nur 10 Takte. Ist das so richtig, oder hast du dich verzählt?

        Ergänzung: Die Gesamtdauer mit 15:89 anzugeben, ist auch seltsam. Normalerweise würde man das auf 16:29 normalisieren. Das sind 989 Sekunden - und 127 Takte in 989 Sekunden? Das bedeutet, ein Takt ist fast acht Sekunden lang. Ehrlich??
        Ich kenne sonst die Faustregel, dass eine Viertelnote einer Silbe bei normalem Sprech- oder Singtempo und damit etwa eine Viertelsekunde entspricht. Bei 4/4-Takt wäre das dann rund 1 Takt pro Sekunde.

        Einen schönen Tag noch
         Martin

        --
        "Hab ich vergessen" ist oft nur ein Euphemismus für "Hab ich noch nie verstanden".
      2. @@Linuchs

        PS: Wer übt, muss nicht immer bei Takt 1 anfangen

        Wer übt, der bescheißt die Kollegen. (alte Musikerweisheit)

        🖖 Живіть довго і процвітайте

        --
        „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
        — @Grantscheam auf Twitter
  2. Hallo,

    Mein Stück hat 127 Takte, jeder Takt belegt horizontal also calc( 100% / 127 ) Prozent.

    Das mag für dein Stück gelten. Das hat dann aber keine Taktwechsel, Ritardandos, Fermaten, ...

    Gruß
    Kalk

    1. Das hat dann aber keine Taktwechsel, Ritardandos, Fermaten, ...

      Ja, ich habe ein ungeklärtes Problem. Habe eine Ablaufgrafik hinzugefügt. Grün sind die aktiven Takte (keine Pause für diese Stimme):

      Der Snapshot zeigt den Einsatz des Basses eine s zu früh. Takt 50 wäre richtig. Eine Sekunde (zähle ein-und-zwan-zig) zu früh in eine Pause reinsingen kann nicht akzeptiert werden.

      Deshalb die rot-grauen Balken zum ausmessen. Oben korrekt 127 Takte in 12 10 Takt Einheiten +, darunter korrekt 4:03 min in 24 10s Einheiten +.

      Die Gesamtdauer 4:03 der Masterspur (erste) ermittle ich mit

      arr_ausgewaehlte_audios[0].addEventListener( 'canplaythrough', function () {
        durationSec = this.duration.toFixed(2);  // Sekunden mit 2 Nachkommastellen
        let arr     = durationSec.split(".");
        minuten     = ("00" + Math.floor( arr[0] / 60 )) . slice(-2);
        sekunden    = ("00" + ( arr[0] - minuten * 60 )) . slice(-2);
        if ( document.querySelector( "#duration" ) ) {
          document.querySelector( "#duration" ).innerHTML = minuten +":" +sekunden;
        }
      });
      

      und die laufende Zeit mit

      var audioTimer = setInterval( getCurrentTime, 200 );
      ...
      function getCurrentTime() {
        runningTime = arr_ausgewaehlte_audios[0].currentTime.toFixed(2);
        // #laufbalken weitersetzen, durationSec (Sekunden mit 2 Nachkommastellen)
        var arr     = runningTime.split(".");
        minuten     = ("00" + Math.floor( arr[0] / 60 )) . slice(-2);
        sekunden    = ("00" + ( arr[0] - minuten * 60 )) . slice(-2);
        if ( document.querySelector( "#laufbalken" ) ) {
          document.querySelector( "#laufbalken" ).style.left  = 100 / durationSec * runningTime +"%"; // hundertstel Sekunden
          document.querySelector( "#laufbalken" ).innerHTML   = " " +minuten +":" +sekunden;
      }
      

      Habe überlegt, ob da ein Rundungsfehler vorliegen kann, aber nicht gefunden. Eine andere Frage wäre, ob das Programm MuseScore genau gleiche Taktlängen beim mp3-Export erzeugt.

      Als ich die Bass-mp3 als Master genommen habe, ist die duration 04:04

      1. Hallo,

        Ja, ich habe ein ungeklärtes Problem.

        Nein. Du möchtest etwas Lebendiges durch Technik abbilden und/oder problematisieren. Das kann nicht funktionieren.

        Gruß
        Kalk

      2. Hallo,

        ich beantworte mal die im Posting-Titel gestellte Frage direkt: Das mp3-Audioformat hat ein sehr exakt deterministisches Zeitverhalten. Zwei mp3-Dateien, die mit derselben Abtastrate und derselben Bitrate erzeugt wurden, müssten also perfekt synchron zueinander abgespielt werden.

        Habe eine Ablaufgrafik hinzugefügt. Grün sind die aktiven Takte (keine Pause für diese Stimme):

        Wo kommt diese Grafik her? Wie bzw. woraus entsteht die?

        Der Snapshot zeigt den Einsatz des Basses eine s zu früh. Takt 50 wäre richtig. Eine Sekunde (zähle ein-und-zwan-zig) zu früh in eine Pause reinsingen kann nicht akzeptiert werden.

        Nein, natürlich nicht. Auch wenn das ein sehr langsames Musikstück sein muss (ein Takt muss ja etwa zwei Sekunden lang sein), geht das gar nicht.

        Deshalb die rot-grauen Balken zum ausmessen. Oben korrekt 127 Takte in 12 10 Takt Einheiten +, darunter korrekt 4:03 min in 24 10s Einheiten +.

        Okay. Beim Abbilden von Taktnummer auf Spieldauer entsteht aber schonmal ein Rundungsfehler, denn 127/243 (Takte zu Sekunden) ergibt irgendeinen gebrochenen Wert. Und ich sehe dich immer nur mit ganzen Sekunden hantieren. Außerdem hat eine Musik-Aufnahme ja meist noch etwas Zugabe vor dem ersten sowie nach dem letzten Takt.

        Die Gesamtdauer 4:03 der Masterspur (erste) ermittle ich mit

        arr_ausgewaehlte_audios[0].addEventListener( 'canplaythrough', function () {
          durationSec = this.duration.toFixed(2);  // Sekunden mit 2 Nachkommastellen
          let arr     = durationSec.split(".");
          minuten     = ("00" + Math.floor( arr[0] / 60 )) . slice(-2);
          sekunden    = ("00" + ( arr[0] - minuten * 60 )) . slice(-2);
          if ( document.querySelector( "#duration" ) ) {
            document.querySelector( "#duration" ).innerHTML = minuten +":" +sekunden;
          }
        });
        

        Warum wandelst du die Spieldauer erst auf eine Darstellung mit zwei Nachkommastellen, wenn du doch die Nachkommastellen gar nicht berücksichtigst? Schon wieder ein möglicher Rundungsfehler, der auf fast eine Sekunde anwachsen kann. Aber solange der Algorithmus der Umwandlung immer der gleiche ist, muss auch das gleiche Ergebnis rauskommen - mit dem gleichen Rundungsfehler.

        Habe überlegt, ob da ein Rundungsfehler vorliegen kann, aber nicht gefunden. Eine andere Frage wäre, ob das Programm MuseScore genau gleiche Taktlängen beim mp3-Export erzeugt.

        Ich habe zwar den Trick mit dem bit reservoir nicht hundertprozentig verstanden - eventuell könnte dadurch bei unterschiedlichen Audio-Inhalten ein kleiner Versatz entstehen. Der wäre dann aber entsprechend den maximal 511 Bytes in der Größenordnung von Millisekunden; ich glaube nicht, dass man das wahrnehmen würde.

        Als ich die Bass-mp3 als Master genommen habe, ist die duration 04:04

        Ich würde doch von Rundungsfehlern ausgehen. Leg doch mal alle deine Zeitberechnungen auf Millisekunden aus und schau nach, ob der Fehler dann immer noch da ist.

        Einen schönen Tag noch
         Martin

        --
        "Hab ich vergessen" ist oft nur ein Euphemismus für "Hab ich noch nie verstanden".
        1. Hallo Martin,

          Außerdem hat eine Musik-Aufnahme ja meist noch etwas Zugabe vor dem ersten sowie nach dem letzten Takt.

          Das dürfte das Hauptproblem sein, daran habe ich bei meinem Vorschlag gar nicht gedacht. Wenn man mehrere Spuren hat, könnten die jeweiligen Spur-MP3s sogar noch verschiedene Zugaben haben. Und es könnte auch noch ein Lied mit Auftakt sein!

          Heißt: Man muss pro MP3 einen Kalibrationsoffset haben, und pro Song die Info, wieviele Auftakte drin sind.

          An so Merkwürdigkeiten wie Ritardando oder einen Dirigenten, der keinen stabilen inneren Takt hat, mag ich gar nicht denken…

          ..., function () {
            durationSec = this.duration.toFixed(2);  // Sekunden mit 2 Nachkommastellen
            let arr     = durationSec.split(".");
            minuten     = ("00" + Math.floor( arr[0] / 60 )) . slice(-2);
            sekunden    = ("00" + ( arr[0] - minuten * 60 )) . slice(-2);
            if ( document.querySelector( "#duration" ) ) {
              document.querySelector( "#duration" ).innerHTML = minuten +":" +sekunden;
            }
          });
          

          Dieser Code hat gleich mehrere Macken - aber die dürften nicht unbedingt zum Versatz führen.

          • durationSec ist nicht deklariert - Absicht?
          • Text weist man an textContent zu, nicht an innerHTML
          • Wenn überhaupt, rundet man ab, indem man Math.floor(this.duration) aufruft. In einen String verwandeln und den am Punkt zerhacken ist sicher nicht bester Code.
          • Der Grund für's Abrunden dürfte darin bestehen, dass sonst die Formatierung der Sekunden nicht passt. Aber das ist doch eine "display only" Anzeige; wie er die Platzierung der Elemente durchführt, geht daraus nicht hervor.
          • Eine mm:ss oder mm:ss.ff Anzeige könnte man eleganter so bilden:
            const durationElement = document.querySelector( "#duration" );
            if (durationElement) {
              const minutes = Math.floor(this.duration) / 60,
                    seconds = this.duration % 60;             // sic! 
                    mmFormatter = Intl.NumberFormat("de-DE", 
                                       { "minimumIntegerDigits": 2 }),
                    ssFormatter = Intl.NumberFormat("de-DE",
                                       { "minimumIntegerDigits": 2,
                                         "minimumFractionDigits": 2,
                                         "maximumFractionDigits": 2 } );
          
              durationElement.textContent = mmFormatter.format(minutes)
                                          + ":"
                                          + ssFormatter.format(seconds);
            }
          

          Ich habe einige Zeilenumbrüche und Temp-Variablen eingefügt, um die Lesbarkeit im Forum zu verbessern.

          Und ja, NumberFormat ist mühsam zu konstruieren. Für sowas kann man sich aber auch einen Wrapper bauen - diese Formatiererei passiert ja öfters.

          class LFormatter {
             static decimalFormatter(vorkomma, nachkomma=0, leadingZero=false) {
                let options = {
                  minimumFractionDigits: nachkomma,
                  maximumFractionDigits: nachkomma };
                if (leadingZero) {
                  options.minimumIntegerDigits = vorkomma;
                }
                return Intl.NumberFormat("de-DE", options);
             }
          
             static getMmSsFormatter() {
                let mf = LFormatter.decimalFormatter(2, 0, true);
                let sf = LFormatter.decimalFormatter(2, 2, true);
                return function(time) {
                  return mf.format(time / 60) + ":" + sf.format(time % 60);
                }
             }
          }
          

          Und dann sieht es im Eventhandler so aus:

          ..., function() {
             let durationElement = document.querySelector( "#duration" );
             if ( durationElement ) {
                durationElement.textContent = LFormatter.getMmSsFormatter().format(this.duration)
             }
          }
          

          Rolf

          --
          sumpsi - posui - obstruxi
          1. @@Rolf B

            • Eine mm:ss oder mm:ss.ff Anzeige könnte man eleganter so bilden:
                durationElement.textContent = mmFormatter.format(minutes)
                                            + ":"
                                            + ssFormatter.format(seconds);
              }
            

            Stringkonkatenation mit + würde ich nicht elegant nennen. Dafür gibt’s template literals:

                durationElement.textContent
                  = `${mmFormatter.format(minutes)}:${ssFormatter.format(seconds)}`;
            

            🖖 Живіть довго і процвітайте

            --
            „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
            — @Grantscheam auf Twitter
            1. Hallo Gunnar,

              ich wollte Linuchs nicht zu viel Neues auf einmal zumuten... 😉

              Rolf

              --
              sumpsi - posui - obstruxi
            2. Hi,

                  durationElement.textContent = mmFormatter.format(minutes)
                                              + ":"
                                              + ssFormatter.format(seconds);
                }
              

              Stringkonkatenation mit + würde ich nicht elegant nennen. Dafür gibt’s template literals:

                  durationElement.textContent
                    = `${mmFormatter.format(minutes)}:${ssFormatter.format(seconds)}`;
              

              das ist zwar anders, aber nicht besser.
              Für mich schwieriger zu lesen und zu verstehen. Das ist aber vermutlich Gewohnheitssache.

              Einen schönen Tag noch
               Martin

              --
              "Hab ich vergessen" ist oft nur ein Euphemismus für "Hab ich noch nie verstanden".
              1. @@Der Martin

                Stringkonkatenation mit + würde ich nicht elegant nennen. Dafür gibt’s template literals:

                das ist zwar anders, aber nicht besser.
                Für mich schwieriger zu lesen und zu verstehen. Das ist aber vermutlich Gewohnheitssache.

                Vermutlich. Ein Überbleibsel aus einer Zeit, als es noch keine template literals gab.

                Es entspricht aber nicht dem mentalen Modell, welches man™ von der Aufgabe hat. Man will ja nicht mehrere miteinander verkettete Strings als Elementinhalt, sondern einen String, der Platzhalter für Dinge enthält, die man von außen reingeben kann. Einen template string eben. Ein template literal, literally.

                🖖 Живіть довго і процвітайте

                --
                „Im Vergleich mit Elon Musk bei Twitter ist ein Elefant im Porzellanladen eine Ballerina.“
                — @Grantscheam auf Twitter
        2. Hi nochmal,

          Ich habe zwar den Trick mit dem bit reservoir nicht hundertprozentig verstanden

          doch, ich glaube, jetzt habe ich ihn verstanden: Wenn der Encoder bei einem Frame wegen plötzlich auftretender akustischer Unregelmäßigkeiten (Knall, Schlagzeug o.ä.) einen höheren Bedarf an Daten feststellt, als die Bitrate eigentlich pro Frame hergibt, dann kann er rückwirkend die letzten paar Frames nochmal neu codieren, dabei den Kompressionsfakror erhöhen und die dadurch frei werdenden Bytes für eben dieses "bit reservoir" benutzen. Damit kann er über mehrere Frames hinweg bis zu 511 Bytes "ansparen", die dann dem besonders ereignisreichen Frame zusätzlich zur Verfügung stehen.

          Dadurch entsteht aber kein Zeitfehler, keine Verschiebung irgendeines akustischen Ereignisses. Fürs Timing ist das also komplett irrelevant.

          Einen schönen Tag noch
           Martin

          --
          "Hab ich vergessen" ist oft nur ein Euphemismus für "Hab ich noch nie verstanden".