Repository navigation
Fill the LUFS column on connect from what the history already knows - #159
Closed
AliceLafox wants to merge 15 commits into
Closed
AliceLafox wants to merge 15 commits into
AliceLafox wants to merge 15 commits into
Conversation
…connect scan The connect scan reads the two memory files and lists directories, and nothing it carried could tell one file in a slot from another without reading it (#141). SlotRow now carries TakeFacts for track 1's first take: its name, its size and the platform's own modification time, read the way the history stores it (FileTime.h), so the two compare as equal integers. No audio is read for them. An entry the file system will not describe whole - a folder under a take's name, a name with nothing behind it, a link to nothing, a stamp that reads as the epoch, which is JUCE's answer for a file it cannot stat - carries no facts: the slot keeps its name on screen and nothing is inferred about it, rather than the scan failing on one take. The facts are part of the snapshot, so a rewritten take is a changed snapshot. take_facts_tests runs the real scan over a scratch volume; the two harnesses that build rows by hand name the new field.
… what it measured hashOfSighting answers with the hash of the take a card's slot holds when the slot's newest row for that file on track 1 carries a hash, its size and its stamp are the entry's to the millisecond, and the body recorded with it says the same WavLen - and only while nothing else about the slot was recorded after that row. A clear, a swap or a failed write after it makes the row history: the pedal stamps what it records with a clock that does not keep the date, so a loop of the same length recorded again in the slot can carry the very same name, size and stamp. A row without a stamp, older than version 9, gets no allowance here as it does in hashHeldBefore: nothing is at stake but a number on screen, and it must not be a guess. cardFor finds a card by its marker without touching last_seen. inferLoudness (InferredLoudness.h) joins that to the readings of #140: a slot the history cannot vouch for, or whose bytes it never measured, is not in the answer, and each answer names the sighting it answers. Neither writes anything. The store suite takes each fact off by one, the newest row, the rows after it that end its word and the ones that do not (an operation only about the slot, another slot's write), track 2, another card, and checks that asking wrote nothing. inferred_loudness_tests records real commands through withHistory: a rename keeps the reading, a swap carries a take under its own name and loses one under the pedal's name, a trim and a clear end it. And a different take under the very same four facts fools it - while the store knows nothing of the bytes really there, and normalize, which reads them, raises the loop the guess says is already at target (#142).
…came from in its hint A LoudnessCell now says whether its number was read off the card or came out of the history for the bytes the slot appears to hold (#141). fromHistory wears a measured cell's words and verdict with the provenance first in the hint: when it was measured, and that it was not re-measured now. Any reading set over it replaces it; a mutation's clearing takes it away like any cell; a dropped read clears only a pending cell, never this. It draws in the same ink at 60%: the column's own way of saying "less present", as the dash and the empty rows do, rather than a mark that in a 68 px number column would read as part of the number. The LUFS cell now shows its reading's hint on hover; a read in flight and a slot with no loop have none. slot_table_harness checks the flag, the hint, the replacement and the clearing; harnesses need a window server and are not run here.
The column started empty on every connect and filled only where something read a take (#141). Now, when a snapshot lands and when a job's busy state drops - the job that reads the card's name included - every slot with a take and an empty cell is put to the store by its scan facts and WavLen, in a quiet background job that needs no card. A reading the history has for those bytes lands as an inferred cell, and only for a slot that still shows the very facts sighted, on the card its marker named, into a cell still empty: a read that came first, or a mutation's clearing, outranks it. It holds off while a foreground job runs, as restoreListening does: the job's rescan lands before its result clears the cells it moved, so the busy drop is the hook after a job and the snapshot hook covers mounts and idle rescans. The player row leaves an inferred cell alone, since its own pass reads the loaded bytes and replaces it. An inferred cell informs the eye and decides nothing: no write path reads the column, and normalize measures the card's bytes before it writes.
The connect scan asked three stats of every take on every 1.5 s poll over a USB mount: is it a file, how big, and when written - each free to see a different file (review of #141). history::statFile asks once and answers both or nothing, and modifiedMs is that same answer's time, so the scan, the history rows and the player's pass all read one number. The number is the one JUCE's getLastModificationTime gives on each platform - the milliseconds on macOS, whole seconds on Linux, the FILETIME's on Windows - so stamps stored before compare equal. A direct stat also tells a stamp of zero from no answer at all, which JUCE could not: an epoch stamp is now described as it is, and only an entry that gives no answer has no facts.
… and size a guess Three ways the history vouched for a file it had not seen (review of #141): - A migrated store's rows have no stamp and vouch by name and size, so that a 0.9.5 history stays restorable. recordWhatSlotsHold then wrote that hash beside the file's fresh stamp, and the next connect trusted the row as naming the very file: a take overwritten outside the app with one of the same size, then renamed, showed the old loop's number (S1). heldBefore now hands such a hash on with no stamp - restorable through the allowance, never inferred - and the stamp is the file's in every other case. The comment that said the staleness stays confined said what this makes true. - The pedal's clock does not keep the date, and in the app's own layout the size follows the length, so a loop of the same length recorded again keeps name, size, stamp and WavLen. Check loudness measured the new bytes and the next connect showed the old reading anyway (S3). Store version 10 adds take_sightings: what a real read saw, the file's facts with the hash of the bytes read under them, by card and slot. readSlotLoudness files one when the file stood still under the read, and the player's pass hands its facts up with its hash (one stat before the decode, one after) for keepReading to file. hashOfSighting answers a read of these facts made after the slot's last operation first, and falls back to the row; forgetting a slot takes its sightings along. - A write that failed after its take landed left a row that still answered. Only a finished operation's row does now, and a first sighting's, which commits slot by slot. And one unreadable recorded body no longer fails the whole job (and a banner on every snapshot): inferLoudness reports that slot and asks the rest, and the app logs it once a connection.
An inferred cell kept the reading's attention colour, which says "Normalize would change this" - a verdict, painted on a number nobody read today (review of #141). It now carries no attention: the number alone, in light ink. Its hint promised "Selecting the slot or Check loudness reads it again", and twice that was not true: the slot already in the player is not read again on selection, and a two-track memory on a card this app only reads has no Check loudness. It now names the one gesture that measures on every card and every slot: "Double-click to measure it now." The table also says whether its LUFS column is shown, for the app to leave a hidden column alone.
…ead's slot a dash The slot loaded in the player was refilled as inferred after an Undo, Redo, Swap or Restore on another slot cleared every cell, and clicking its row did nothing: the player does not read again a file it already has (review of #141). The inference now never answers for the player's slot. It is the one slot the app can measure at once, so when nothing is reading it, the existing background read does. A guess no longer outlives a read that could not confirm it. A player pass that cannot measure the file tells the owner, and the inferred cell goes; that file, and the file of a failed Check, is not asked of the history again in this connection. A normalize meters the slot's bytes on its way, the bulk run included: the recorder hands the reading on, and it replaces the cell as a measurement - a write that follows clears it, as every rewrite does. Nothing is inferred while the LUFS column is hidden; showing it asks the history then.
The column takes a normalize's measurement as a measured cell through HistoryRecorder::onMeasured; the fooled-take case now checks the hook is called once, with the slot and the very number normalize decided by.
…eat/lufs-from-history
…its hash on Two holes left in how the history believed a read (re-review of #141): - An operation after a read brought the wrong number back. heldBefore read only slot_audio: after Check loudness had read a frozen-clock re-recording, a rename carried the old row's hash under the very same facts into a row newer than the read, and the next connect trusted it (S3d). heldBefore now takes the newest word for the file: a read of these facts that no earlier operation on the slot came after hands on the hash of the bytes it read, with the file's stamp. - Reads were ordered against operations by wall clock. A first sighting resumed on a later connect keeps the time it began (S7), a player read is filed by a background job a foreground operation can jump ahead of (S8), and a clock can be set back (S10). A sighting now keeps since_op: the slot's newest operation at or below the newest one begun when the read started. It is history once an operation with a higher sequence records a body or a take in the slot; a subject alone does not count, nor another slot's operation. The check job notes where its read began at its start, the player when it starts a pass on the card's take (HistoryRecorder::newestOp, published as operations begin), and the late filing carries it. A first sighting photographing a slot removes that slot's earlier reads: its sequence is old, its photograph is not. take_sightings gains since_op in version 10, which has not shipped.
… out of it Three small ones from the re-review of #141. A slot remembered as unreadable stayed so for the connection even after a read of it succeeded: any successful read of the slot now lifts the mark. A Check the lifecycle gate turned away - the pedal leaving, a ghost mount - never reached the file, yet marked it unreadable: the check job now marks only a failure that came after the gate let it through to the card. And the player's slot was read in the background even when it held a multi-track memory playing as a mix, which the player never measures: track 1's number is not the mix's, and nobody asked for the banner a failure raises. A mix is now any other slot to the column.
…ects the column A normalize measures the slot's bytes before it decides, and its hook filed the reading and told the column - but not what it had read. One that found the slot at target writes only a subject, so the next connect still trusted the row that named the file under the same frozen-clock facts and showed the old take's number (round 3 review of #141, S12: measured -27.997, nothing applied, next connect -19.997). The hook now files a sighting too: the take's facts from one stat, under the hash of the bytes measured, placed just before the operation it is part of. A normalize that then writes records the archived and the landed take with its own, higher sequence, which makes its read history; one that changes nothing leaves the read standing for the next connect.
beginOp took max(seq) + 1, so forgetting a slot's history that dropped the newest operations whole handed their numbers out again, and a read placed after "operation N" could then stand after an operation that began later (round 3 review of #141, S11b). Not reachable through the app today - the forget's own maintenance operation holds the maximum - but the order of reads must not rest on that. Version 10, which has not shipped, adds op_sequence: one row, the highest sequence ever issued, started from what the store holds and moved by a trigger on every insert into ops. beginOp issues the next value past it (and past the Undo floor, as before), and newestOp reads it, so neither goes back when operations are forgotten - across a reopen too.
…n words
take_facts_tests aborted on the Linux lanes: the connect scan's doctor pass
reads takes through readFileBytes, and a folder under a take's name opens as
a stream on Linux, after which libstdc++ throws std::ios_base::failure from
the first read ('Is a directory'). Nothing expects that exception; on a card
it would have ended the worker's scan. macOS's library fails the stream
quietly, so the suite passed there. readFileBytes now refuses a path that is
not a regular file with an Error before a stream exists.
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #141. Stacked on #157 (
feat/loudness-in-history, schema v9); the base moves on as the stack below it is merged. Parallel to #158 (#142) on the same base; the two meet inapp/history/SlotLoudness.hand the normalize hook inHistoryRecorder.cppand are merged by hand when both land.The LUFS column started empty on every connect. A connect scan reads no audio, and proving identity by hash would mean reading every take. So the free facts decide: the take's name, size and modification time from its directory entry, plus
WavLenfrom the config.app/TakeFacts.h,statFile); no audio is read.HistoryStore::hashOfSighting,app/history/InferredLoudness.h): the slot's newest after-row for that file name, from a finished operation, with an exactly equal size and stamp (a migrated row without a stamp never matches) and the sameWavLen, with nothing recorded about the slot after it — or a real read of these exact facts that no later operation on the slot has voided. The reading under that hash (Loudness readings belong to the bytes: keep them in the history, keyed by content hash #140) is shown.take_sightings, placed among operations by sequence, not by clock): the check job, the player's filed pass and a normalize's measurement. So a take re-recorded on the pedal with the same name, size and stamp (a pedal clock that does not tick) is corrected by the first real read and stays corrected across later operations and connects. A resumed first sighting voids the slot's earlier reads; operation sequence numbers are never reused (op_sequence).needsVolume = false), skipped while the LUFS column is hidden; one unreadable recorded body skips its slot only.Tests:
take_facts_tests(new),inferred_loudness_tests(new: the frozen-clock re-record, migrated rows, swaps of pushed and pedal-named takes, a failed write, a resumed baseline, a late-filed read, a clock set back, a normalize at target),history_store_tests(sighting order by sequence, the done rule, forget),history_schema_tests(v9→v10). Full ctest 67/67 locally, the six GUI harnesses included.Known and accepted: FAT stamps are local time, so after a DST change the exact match fails and cells stay a dash (the safe direction); a hardware spot-check of whether the RC-5's clock ticks would settle how often the first case occurs.