Changeset 5e72413 in Klonkt


Ignore:
Timestamp:
08/22/2026 11:23:57 AM (2 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
1e194e4, fbacddc
Parents:
c0e463b
Message:

Seek over de hele band (shaer-tmyn, stap 4 en laatste)

De balk TOONDE al de hele band -- de teller rekent sinds c0e463b met de
trackduren -- maar aanklikken seekte binnen het lopende nummer. En de keten
houdt het lopende nummer plus een vooruit, dus op een band van twaalf nummers
reikte die balk in werkelijkheid tot nummer twee. Een balk die belooft wat hij
niet doet is erger dan geen balk.

bandPositie(ratio) rekent de verhouding om naar nummer + positie daarin, met
dezelfde duren als de teller. Ligt het doel in het lopende nummer, dan wordt de
positie gewoon verzet: geen herlaadsprong voor een paar seconden. Ligt het
verderop, dan zet hij pendingSeek en laadt dat nummer -- exact de weg die het
sessieherstel al gebruikt -- met de speelstand mee, zodat springen niet stiekem
start of stopt.

Er valt een hoorbaar gat bij zo'n sprong. Dat hoort bij spoelen naar een plek
die niet in de buffer zit, en het is eerlijker dan een balk die daar nooit komt.

DEZELFDE OMREKENING IS OOK DE ENIGE DIE OP iOS KAN: de blob-motor heeft geen
doorlopende tijdlijn, dus daar bestaat "seek over de keten" niet eens. Nu delen
beide motoren een model in plaats van twee.

Nagemeten op dev, klikken op vijf plekken van de balk (band van 373 s,
nummers van 103, 144 en 126 s):

10% -> 39 s, nummer 1 50% -> 189 s, nummer 2
25% -> 95 s, nummer 1 85% -> 319 s, nummer 3
95% -> 356 s, nummer 3

Steeds twee seconden voor op de verwachting, en dat is het doorspelen tijdens
het meten zelf.

audio-player v39.

Co-Authored-By: Claude Opus 5 <noreply@…>

Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/assets/js/audio-player.js

    rc0e463b r5e72413  
    885885  }
    886886
     887  /**
     888   * Waar op de BAND ligt deze verhouding? Geeft het nummer en de positie erin.
     889   *
     890   * Dit is de hele truc van hele-band-seek. De balk toont al de hele band (de
     891   * teller rekent met de trackduren), maar aanklikken werkte binnen het lopende
     892   * nummer -- en de keten houdt maar een nummer vooruit, dus een balk die de
     893   * hele band belooft reikte in werkelijkheid tot nummer twee. Dat is erger dan
     894   * geen balk, want het ziet eruit alsof het werkt.
     895   *
     896   * Omrekenen kan alleen met de echte duren, en die hebben we sinds de teller
     897   * uit bandDuur()/bandOffset() komt. Dezelfde omrekening werkt op BEIDE
     898   * motoren: de blob-motor heeft helemaal geen doorlopende tijdlijn, dus daar
     899   * is dit niet alleen de beste maar de enige manier.
     900   */
     901  function bandPositie(ratio) {
     902    const totaal = bandDuur();
     903    if (!totaal) return null;
     904    const doel = Math.max(0, Math.min(ratio, 1)) * totaal;
     905    for (let i = 0; i < queue.length; i++) {
     906      const start = bandOffset(i);
     907      const eind = start + (Number(queue[i].duration) || 0);
     908      if (doel < eind || i === queue.length - 1) {
     909        return { index: i, binnen: Math.max(0, Math.min(doel - start, (Number(queue[i].duration) || 0) - 0.25)) };
     910      }
     911    }
     912    return null;
     913  }
     914
    887915  function attachSeek(seekEl) {
    888916    seekEl.addEventListener('click', (e) => {
    889917      const rect = seekEl.getBoundingClientRect();
    890918      const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
     919
     920      // BANDMODUS: de balk is de hele band, dus de klik ook.
     921      const plek = tapeMode ? bandPositie(ratio) : null;
     922      if (plek) {
     923        if (plek.index === currentIndex) {
     924          // Binnen het lopende nummer: gewoon verzetten, geen herlaadsprong.
     925          if (useMse() && chain.length) {
     926            const seg = currentSegment();
     927            if (seg) { try { audio.currentTime = seg.start + plek.binnen; } catch (er) {} }
     928          } else {
     929            try { audio.currentTime = plek.binnen; } catch (er) {}
     930          }
     931          updatePositionState();
     932          return;
     933        }
     934        // Een ander nummer: laden en er meteen in springen. pendingSeek is de
     935        // bestaande weg daarvoor -- het sessieherstel doet precies dit -- en
     936        // wordt toegepast zodra de metadata er is. Er valt een hoorbaar gat bij
     937        // de sprong; dat hoort bij spoelen naar een plek die nog niet in de
     938        // buffer zit, en is eerlijker dan een balk die daar niet komt.
     939        pendingSeek = plek.binnen;
     940        loadTrack(plek.index, !audio.paused);
     941        return;
     942      }
     943
    891944      if (useMse() && chain.length) {
    892945        // Seek within the CURRENT track's segment of the chain timeline.
  • src/views/shell.ejs

    rc0e463b r5e72413  
    406406     ?v=N — cache-buster: bump bij elke audio-player.js wijziging zodat
    407407     Cloudflare (max-age=1y) niet de oude versie blijft serveren. -->
    408 <script src="/assets/js/audio-player.js?v=38"></script>
     408<script src="/assets/js/audio-player.js?v=39"></script>
    409409<!-- Eigen custom media-embeds (YouTube/SoundCloud/Spotify) via de echte
    410410     player-API's + gedeelde mutual-exclusion registry met de site-speler. -->
  • test/mixtape-ui.test.js

    rc0e463b r5e72413  
    245245    'zonder dit las de teller de lengte van wat toevallig gebufferd was');
    246246});
     247
     248test('de balk is in bandmodus de hele band (shaer-tmyn, stap 4)', () => {
     249  const speler = fs.readFileSync('src/assets/js/audio-player.js', 'utf8');
     250
     251  // De omrekening ratio -> nummer + positie erin. Zonder dit seekt de balk
     252  // binnen het lopende nummer, terwijl hij de hele band TOONT: een balk die
     253  // belooft wat hij niet doet is erger dan geen balk.
     254  assert.match(speler, /function bandPositie\(ratio\)/);
     255  assert.match(speler, /const plek = tapeMode \? bandPositie\(ratio\) : null;/);
     256
     257  // Zit het doel in het lopende nummer, dan geen herlaadsprong -- anders hoor
     258  // je een gat bij het verzetten van een paar seconden.
     259  assert.match(speler, /if \(plek\.index === currentIndex\)/);
     260
     261  // En anders langs pendingSeek, dezelfde weg die het sessieherstel gebruikt.
     262  assert.match(speler, /pendingSeek = plek\.binnen;\s*\n\s*loadTrack\(plek\.index, !audio\.paused\);/,
     263    'de speelstand hoort mee te gaan: springen mag niet stiekem starten of stoppen');
     264
     265  // De niet-bandmodus mag hier niets van merken.
     266  assert.match(speler, /if \(useMse\(\) && chain\.length\) \{\s*\n\s*\/\/ Seek within the CURRENT track/,
     267    'het oude gedrag hoort te blijven staan voor een gewone wachtrij');
     268});
Note: See TracChangeset for help on using the changeset viewer.