Repository navigation
anlz: beat grids, phrases and waveforms written into tracks' analysis files, and analyzer plugins that work them out - #90
Merged
Merged
Conversation
…es, new files made for a track without any, and 3-band waveforms read for display
…where a drive has none and written to its analysis files as a journaled, undoable edit
…filling memory, and refused analysis edits, overlays and damaged files have properties
# Conflicts: # apps/locallibrary/docs/rpc/schema.json # apps/locallibrary/src/plugins.rs # crates/locallibrary/src/bin/test-plugin/main.rs # crates/locallibrary/src/plugin.rs # crates/loclib-plugin/src/manifest.rs # crates/loclib-plugin/src/manifest/tests.rs
…e every plugin it serves has
# Conflicts: # apps/locallibrary/docs/commands.md # apps/locallibrary/src/plugin_cmd/analyze.rs # crates/locallibrary/src/anlz.rs # crates/locallibrary/src/bin/test-plugin/main.rs # crates/locallibrary/src/edit.rs # crates/locallibrary/src/plugin.rs # crates/loclib-plugin/README.md # crates/loclib-plugin/src/lib.rs # crates/loclib-plugin/src/sdk/tests.rs # crates/loclib-plugin/src/sdk/tests/events.rs
Coverage Report for apps/web
File CoverageNo changed files found.SizeCompared with main. KiB are 1,024 bytes.
|
…ers and analysis edits' warnings and replays have properties
The analysis files and the staged waveform are read through fsutil::read, which main now uses for files renamed over while they may be read.
Rust coverageFrom run 275. Both are held to the thresholds in .cargo/config.toml.
|
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.
What and why
This builds on the plugin system from #67, #68, #74, #79, #82 and #85.
locallibrary could read a track's beat grid, phrases and waveforms from its analysis files (
ANLZ0000.DAT,.EXT,.2EX), but the only things it could write there were cue points and a whole-grid time shift. An analyzer plugin could only answer columns (numbers, strings, booleans). With this PR locallibrary writes beat grids, phrases and waveforms into the analysis files as a journaled, undoable edit, and an analyzer can work them out. What an analyzer works out is only drawn, in the player, for a track whose drive has none, until the user asks for it to be written.Writing a section of an analysis file
Beat grids (
PQTZ, in.DAT)write_beat_gridwrites the beats as rekordbox lays them out: header 0x18, the two unexplained header words kept as the file has them (rekordbox 6's for a new section), the count, then each beat's number in its bar, tempo in hundredths of a BPM and time in ms (grid.rs).checked_gridrefuses what the format can't hold: no beats, a beat number outside 1-4, a tempo outside 0.01-655.35 BPM (NaN and infinities included), and beats that aren't strictly in time order (grid.rs)..EXT's extended grid (PQT2) is taken out when a new grid is written (grid.rs). Its body isn't understood well enough to write, and leaving one that contradicts the new grid seemed worse than leaving none. Shifting a whole grid by some milliseconds (the existingshift_times) still keeps it.Phrases (
PSSI, in.EXT)write_song_structurewrites the mood and phrases, or removes them (structure.rs). Rewriting keeps the file's header bytes (lighting bank included) and every byte of each phrase entry whose start, kind and fill-in stay the same, so the variation flags rekordbox set survive. A new entry gets flags of 0.Waveforms (
PWAV,PWV2,PWV3,PWV5,PWV6,PWV7)write_waveformwrites a tag's columns as given, keeping the file's header fields and using rekordbox 6's for a new section (waves.rs, waves.rs). Only whole columns are taken.waveform_from_bandsworks out every tag exceptPWV4from one three-band waveform (lows, mids and highs, 0-255 each, 150 columns a second,Bandsin loclib-model) (waves.rs). The previews are pooled to rekordbox's widths (400, 100 and 1200 columns), each band the loudest of the columns it covers. Blue columns are the loudest band's height in 5 bits with the highs' share as whiteness in 3;PWV2is a 4-bit height;PWV5is red, green and blue (lows, mids, highs against the loudest) in 3 bits each and the height in 5; the 3-band tags are mids, highs and lows, shifted to the 0-63 and 0-127 ranges beat-link draws them from.PWV6andPWV7from the.2EXfor a track that has no other waveforms, drawn in a new 3-band style (anlz.rs).New analysis files
new_analysis_filesmakes a.DATand.EXTfor an audio file: thePMAIheader andPPTHpath as rekordbox 6 writes them, and the empty cue lists it writes for a track without cues, for the writers above to add to (tags.rs). Nothing in this PR uses it yet: the edit below needs a track's existing files. It's there for building a drive from another library, which will need files for tracks rekordbox never analysed.The edit (
set_analysis)Edit::SetAnalysis {track, beats?, phrases?, waveform?}writes any of the three into one track's analysis files (edit.rs, analysis_edit.rs). It's made like a cue edit: the files are backed up first, written through the commit path's atomic replace, and recorded in the journal, so undo puts them back byte for byte andedits replaymakes it again after rekordbox exports to the drive.edit_trackswith the tempo most of its beats have, recorded with it so a replay doesn't add another (edit.rs). Cues stay where they are when the grid moves, and the plan says how many sat on beats of the old grid. A beat past the track's end is refused, and phrases running past the grid's last beat are warned about..locallibrary/waveforms/<hash>.bands, so the journal names it and a replay writes exactly the same tags. The journal keeps only the staged files its entries name and carries them to another drive when it's copied, as it already did for artwork images, which now share that code (staged.rs).set_analysisedit, so a drive that never had one stays readable by older builds (edit.rs)..DATis missing, phrases for a track with no.EXT, and a waveform for a track whose files hold rekordbox's colour overview (PWV4) or the.2EX'sPWVC(analysis_edit.rs). Those two can't be worked out from another waveform, so that track's waveforms stay rekordbox's. An analysis path outsidePIONEER/USBANLZ/is refused too, as cue edits already refuse it.Analyzers that work them out (the
analysisfeature)beats,phrasesorwaveform, one of each at most (hooks/analyzers.rs, hooks/analysis.rs). A waveform's value is{blob, columns_per_sec}, the blob a file in the request's scratch folder holding the packed bands.Output::ofreads a value as its type says, andFieldValue::beats,phrasesandwaveformmake them (hooks/analysis.rs).<store>/cache/overlay/<plugin>/<analyzer>/<version>/(fields/overlay.rs).Plugins::plan_apply_analysisturns what one analyzer kept into an edit plan: oneset_analysisper track that would change, planned and confirmed as any edit is (plugin/analysis.rs). It only writes where a track has none unless asked to overwrite. Phrases count the analyzer's own beats, so they're only written with its grid or onto the same one. Tracks the analyzer hasn't done yet, and those whose files can't take what's asked, are left out with a warning.analysis:plancapability, giving its waveform as a file in its scratch folder, which the host reads as it plans (plugin/operations.rs).Command line
analyze --apply grid,phrases,waveform(with--overwrite) runs the analyzer and then plans writing what it worked out (plugin_cmd/analyze.rs). Keys and BPMs are applied in a run of their own, and several analyzers that could answer must be narrowed to one.plugin listandplugin infodescribeanalysis:planin plain words.Sidecar
plugins.apply_analysis.plan {plugin, analyzer, drive, parts, tracks?, overwrite?}andplugins.apply_analysis.apply(rpc/methods/plugins/analysis.rs).docs/rpc/is regenerated.Player
Design choices
PQT2writer. Its body has two bytes a beat whose first doesn't count beats in the bar in rekordbox 6 exports and whose second nobody has explained, so a written one could only be a guess. A new grid takes it out instead.PWV4writer, and waveform edits are refused for tracks that have one. Its first two bytes per column aren't understood. Removing it would leave a track with a colour detail waveform and no colour overview, and writing the rest beside rekordbox'sPWV4would leave the overview and the detail disagreeing.USBANLZlayout written into the databases as well, which belongs with building whole drives from another library.Bandstype covers what plugins give, every tag is worked out from it, and it draws through the existing two-layer renderer, so merging the colour and mix waveform models wasn't needed to draw plugin waveforms.Beatmodel (beat_number,bpm,time_ms), which the readers and the XML export already use, rather than a second shape for the same thing.How it was tested
New and updated tests:
crates/locallibrary/tests/it/props_anlz_write.rs):rekordbox_files_rewrite_byte_for_byterewrites the grid, phrases and every waveform tag of rekordcrate's real rekordbox 6 files with what they hold and gets the same bytes. Hegel propertiesbeat_grids_read_back_as_written,phrases_read_back_as_writtenandwaveforms_read_back_as_writtenwrite drawn data into real, synthetic and new files and check that the reader and rekordcrate's independent parser read it back, that every other section is unchanged, and that writing again changes nothing.waveforms_are_worked_out_from_bandschecks each tag's mapping,bands_over_a_day_are_refusedthe length limit, andwriters_take_damaged_files_without_panickingdamages section headers and cuts files short. Example tests cover the refusals,changing_a_phrase_keeps_the_others_entriesand the tag order of new files.crates/locallibrary/tests/it/analysis_edit.rs):a_beat_grid_is_written_with_its_bpm_undone_and_replayed,phrases_are_written_into_the_ext,a_waveform_is_written_staged_and_replayed,cues_on_beats_stay_put_when_the_grid_moves,a_waveform_is_refused_for_a_track_with_rekordboxs_colour_overviewandanalysis_that_cant_be_written_is_refused. The Hegel propertyany_grid_phrases_and_waveform_read_back_and_undoapplies drawn grids, phrases and waveforms and restores the backup byte for byte,analysis_edits_that_cant_be_made_change_nothingdraws each way an edit is refused and checks that the drive is unchanged and no waveform is staged, andany_analysis_edit_warns_of_what_it_leaves_and_replays_as_madedraws a moved or kept grid, phrases, a waveform on a track with or without an.EXTand cues on the old grid's beats, checks the warnings for cues left off the new grid and for a waveform with only an overview to go in, then replays the journal on a drive that already has the edit (nothing changes), after rekordbox exports again (the same files, the waveform read from its staged copy) and with the.DATgone (skipped with a warning), or sets a second grid, which has noPQT2left to lose.crates/locallibrary/tests/it/plugins/analysis.rs), running the test plugin, whoseheardanalyzer now answers structured fields:structured_fields_are_kept_apart_from_the_columns,structured_fields_that_arent_what_they_say_fail_the_track,the_analysis_is_written_as_an_edit,only_what_the_analyzer_works_out_is_written,structured_fields_need_the_analysis_featureandan_operations_analysis_edit_takes_analysis_plan, and the Hegel propertieswhat_the_drive_lacks_is_drawn_from_the_overlay,any_structured_answer_is_kept_and_planned_as_given,overlays_read_back_as_kept_and_refuse_what_isnt_theirs,writing_an_analysis_is_refused_for_what_isnt_there,waveforms_over_a_day_fail_the_track,any_structured_answer_is_kept_or_fails_the_track_saying_why(nulls and an empty grid keep nothing; a beat out of its bar, out of tempo range or out of order, and a waveform file that's missing or isn't three bytes a column fail the track with the reason) andany_kept_analysis_is_planned_as_asked_and_left_out_as_said, which draws what the analyzer kept for each track, the parts asked,overwrite, one track or all and whether a track's files can take a waveform, then checks the plan's summary, each track'sset_analysisedit and the warnings for tracks not analysed yet, parts the tracks already have, phrases on another grid and tracks left out, and that the drive matches the plan once it's applied.hellotest cover the new field types, the feature and the capability. loclib-model has tests forBands. The app has command-line tests foranalyze --applywith the new parts and the player's deck line, and the Hegel propertyanalyze_apply_writes_the_parts_asked_where_it_can(apps/locallibrary/tests/it/commands/analysis.rs) runsanalyze --applywith drawn parts,--overwrite,--dry-run,--jsonand tracks whose audio the analyzer fails on. It checks the run line, the plan's summary and warnings, and the grids on the drive afterwards, and that the refused runs (a key or BPM asked alongside,--min-confidence, several analyzers and none named, an analyzer that doesn't work the part out) are refused before anything runs, and that a run where every track fails is an error. A key or BPM asked alone is written from the analyzer chosen for it, the sound plugin's overheard's.Needs testing on players
None of this has been tried on hardware yet. Each needs a drive written by this branch on a CDJ or XDJ:
.EXTlost itsPQT2after a new grid: does a CDJ-3000 (and a nexus 2 player) still load it, show the grid and quantize to it?PQTZgrid: beat and bar markers, sync and quantize land where they should, including a grid with tempo changes.PSSIphrases: the phrase bar on the player, and Lighting mode on a mixer or rekordbox lighting reading them. Also a mid-mood track with exactly 53 phrases, which is written unmasked.PWAV,PWV2,PWV3), colour detail (PWV5) and 3-band (PWV6,PWV7) waveforms look like the track on players that draw each.new_analysis_files(PMAI,PPTH, empty cue lists, then sections in rekordbox's order), once something uses it.Checklist
apps/locallibrary/CHANGELOG.mdhas a line under Unreleased, if people using it will noticedocs/and--helpstill say what the code does (apps/locallibrary/docs/rpc/too, for a sidecar change)