j5v / xetune Goto Github PK
View Code? Open in Web Editor NEWExperimental visualiser and basic editor for multiple microtonal tunings.
Home Page: https://johnvalentine.co.uk/app/xetune/app.html
License: MIT License
Experimental visualiser and basic editor for multiple microtonal tunings.
Home Page: https://johnvalentine.co.uk/app/xetune/app.html
License: MIT License
Currently, the configuration does not persist in localStorage unless the user selects Configure from the toolbar, then Save (without necessarily making changes to the configuration).
As a user, I want configuration to persist with major changes of UI state or data, particularly when:
Perhaps selection of notes can be exempt, or on a debounced timer, so that selections and limits save infrequently.
Currently, the SVG UI is not keyboard navigable, cannot take focus, and therefore provides no hints to screen readers.
Currently, the tool matrix of icons is duplicated for each tuning row. The user selects an icon directly, to operate on the tuning row.
Consider a 'tuning selection' UI, along with a global toolbar that operates on selected tunings.
This would simplify the display, but it would increase the number of gestures required for most operations.
XeTune auto-generates tunings, but the label for ratio-based limits is misleading.
e.g. 9-limit is usually interpreted as a prime limit, but currently, XeTune generates only integer-limit ratio tunings.
As a user, if I find the app won't refresh or won't start because the configuration causes an irrecoverable error, then provide a button that resets the configuration to default.
Provide a reset button in the initial HTML, which is replaced by the render.
This will provide safety for errors that occur until the composed HTML is committed to the DOM, which is the last step in most cases.
config
should contain only configuration state. It currently contains: configuration state, current project data (tunings and their notes), UI state (selections).
This is a 'could-do' issue that helps code comply with the principle of separating concerns. It carries regression risk if implemented wholly. It can be ignored for the time being, but the problem might become significant when we provide explicit interfaces for configuration options.
While it's beneficial to save UI state that the user is invested in, it should really be separated out into UI state, and perhaps persisted as a UI state object, so that invested selections are restored over sessions.
Some UI state, like tunings[].notes[].selected
would need to be separated out, and use serializable references. Most of the code addresses tunings and notes by .id
, but look out for synchronization issues when arrays of objects are mutated.
As a user, I want to specify the Global reference frequency for note 0 of all tunings. After specifying the reference frequency, I want note values to reflect this change, and to be able to re-edit the Global reference frequency in the same way I specified it.
This is a 'should-do' issue, because performance scenarios are often not based on 440Hz, and analysis should reflect the performance scenario.
An explicit UI for configuration of the base frequency, e.g. toolbar icon -> dialog.
Currently, we define this as an offset from 440Hz (A)
const config = {
...
noteRefHz: 440 * (Math.pow(2, -9/12)), // C, around 261 Hz // TODO: split to be configurable
...
}
It might be helpful to provide a friendly interface to specify a reference frequency (Hz) and offsets in other units.
The UI currently shows the global reference frequency config.noteRefHz
at the appropriate point above the tuning rows. So many (nearly all) UI elements depend on config.noteRefHz
, so a call to renderApp()
will refresh these*.
*An event-subscription pattern would also work, but that would be significant effort and change, hard to maintain, and likely less efficient in practice.
In production workflows, a tuning configuration doesn't specify the base frequency of a tuning (Hz of note 0). Rather that's the responsibility of the virtual instrument, tuning controller, or controller that uses a .kbm
(keyboard map file, where reference frequency is embedded).
However, a reference frequency is useful:
The caveat is that XeTune is not responsible for transmitting the reference frequency, because conventional workflows assign this responsibility elsewhere, and the tuning file (in the case of .scl
) cannot ordinarily carry this data. Users need to synchronize the two independent sources of reference frequency.
The named ratios already have a .level
property in ref.noteNamesHarmonic[]
, which indicates their importance in the UI (low values for .level
imply important notes). More important ratios are displayed as heavier lines in the tuning row, and their shorthand labels display above the note button.
However, this applies only for ratios below integer limit 11.
Do we need to calculate the importance of other ratios, or ED generated notes that are near important ratios?
If yes, do we need to dynamically change the thresholds for visual effects? We currently have configurable thresholds for:
config: {
...
noteShowSubtleLinesBelowLevel: 5,
noteShowLabelsBelowLevel: 3,
nearEnoughCents: 8, // for letter labels of notes
...
}
Would an omnisearch improve usability?
Omnisearch is a search box in the toolbar, which searches everything in the app. Selecting a suggestion in a pop-up list triggers an action.
Some examples, as a user:
31-EDO
), and when I select a result, the tuning displays as a new tuning.perfect fifth
, P5
, or G6
. Two actions display for each matching interval name: Select notes, and Create notes. I can select these actions to execute.delete
, and resulting actions like Remove selected notes or Remove selected tunings display. I can select these actions to execute.Some long-term aims for this project, when it is mature enough:
Expand these into issues when ready.
As a user, I want to hear selected notes, as a sequence or as a chord.
MVP: sine tones.
Auditioning within XeTune has advantages:
Find a consistent programmatic way of assigning sharp/flat labels and symbols to non-12-ED notes, especially with tunings having more than 12 divisions.
Consider which generation algorithms to add to XeTune. For example:
Research new tuning series.
As a user, I want to add a note to a tuning, from the Add note button on a tuning row.
MVP: An Add note dialog with a free text field that allows entry of ratio, cents, or Hz. Provide help text to explain this in the dialog.
When the user submits the Add note dialog, populate all the remaining properties of the note. This might best be combined with a refactor of the other code that populates note properties, e.g. the expandTuning
functions.
As a user, I want an analysis of all currently-visible intervals of one selected ED tuning, displayed in a new Tunings tab.
We currently have a hidden tab, and logic to branch out to analysis modes for:
This issue implements content for the function analysisEDTuningIntervalsHTML()
, which currently holds placeholder text.
MVP: reuse the analysis cell from analysisNoteIntervalsHTML()
, but presented as a single column or row of intervals (rather than a grid).
Better: Implement a table with all the details exposed.
This would prevent corruption of the DOM, and execution of arbitrary code.
Fix the tuning names, in the Tuning properties dialog.
A function already exists in the code, escapeHTML()
.
To test check that 9-limit<script alert() />
doesn't corrupt the DOM, and doesn't show an alert.
As a user, I want an analysis of all currently-visible intervals of two selected tunings, displayed in a new Tunings tab.
We currently have a hidden tab, and logic to branch out to analysis modes for:
This issue implements content for the function analysisTwoTuningsTableHTML()
, which currently holds placeholder text.
Reuse the layout and cell content from analysisNoteIntervalsHTML()
. 'Refectoring for reuse' might be beneficial.
As a user, I want to keep tuning tools looking small, while zooming into the tuning scales.
As a user, I want an analysis of all currently-visible intervals of one selected non-ED tuning, displayed in a new Tunings tab.
We currently have a hidden tab, and logic to branch out to analysis modes for:
This issue implements content for the function analysisSingleTuningTableHTML()
, which currently holds placeholder text.
Reuse the layout and cell content from analysisNoteIntervalsHTML()
, except using the same tuning on both the horizontal and vertical axis. 'Refectoring for reuse' might be beneficial.
Which formats should XeTune support?
Some future-gazing questions that might help answer that:
.scl
format loses?My instinct is to await user needs (features) that require file persistence, before reaching for proprietary or compatible formats.
For the ratio-based tuning options, I want controlled imperfections that give specific effects.
This enables octave stretching, shimmer, phase effects, and degrees of dissonance that might make tunings more interesting.
As a user, I want the tunings to remain in view when I scroll horizontally in the Analysis section.
As a user, I work with multiple tunings with different reference frequencies, and want this reflected in analysis, and displayed above each overridden tuning row at the appropriate point on the tuning line. I need an optional reference frequency in each tuning, to override the global reference, and want to edit this in Tuning properties.
Follows from #23.
'Could-do' because fewer users need this.
Split from #13
Perhaps selection of notes can be exempt, or on a debounced timer, so that selections and limits save infrequently.
If further analysis shows that this is all UI state, then close this issue.
Currently, scale generation and interval analysis use a set of ratios that were generated to integer limits, e.g. a maximum value for n and m, for ratio = n / m.
Incorporate different types of limits:
ratiosToPrimeLimit()
already generates ratios to prime limit, but it's not exposed to the UI. It generates too many results, and needs limiting further.
(possibly over-engineered!)
Allow the user to select options of Integer limit, Odd limit, and Prime limit. At least one is required.
Integer limit takes an integer parameter
Odd limit takes an integer parameter
Prime limit takes:
Allow the user to create profiles that store the above parameters. Suggest this UI is opened from the main toolbar.
Provide some default profiles:
Allow choice of these profiles in:
7p999i-limit
, for a 'prime 7-limit' further limited by an 'integer limit of 999'.As a user, I want to reorder the tuning strips.
Consider:
Drag and drop is not accessible for keyboard-only users, so if drag and drop is done, it would be helpful to also provide keyboard-focusable button on each tuning strip that activates a move operation controlled with cursor keys, finalized by Enter/Space keys, and aborted by the Escape key.
As a user, I want to select a range of notes in a tuning, by dragging a box over them.
Move to another issue if these are not completed.
As a user, I don't want to see overlapping notes on a tuning scale.
Note: I'm calling this a bug, because currently, important visual data is obscured. Enhancement with an improved positioning algorithm will fix it.
Where rendering a note in the default location would overlap previous notes, place the note lower in the row band. For each note attempt to place at the top, and repeatedly try lower until there is space.
This requires expanding the row band, and consequently the total size of the tunings SVG.
Analyse whether other patterns and vertical spacings may result in better distributions or keep row expansion to a minimum. If note importance is implemented, see whether that can be used to ensure the important notes are placed at the top of the tuning band.
As a user, I want to view details for the last-touched note in a tuning strip, in a Note properties tab in the analysis section of the UI.
This is the first step; the second step allows robust editing of the note data.
As a user, I want to hear selected notes played with a voiced timbre.
Sine tones can quickly become tiring because they provide fewer 'partials' clues, and need to be played with more energy to be heard and decomposed by the ear. They also don't reflect what is likely to be played by the production instrument.
See also #29 for advantages of auditioning within XeTune.
Before implementing, consider whether it's sufficiently easy to import a XeTune tuning into whatever DAW/sequencer/instrument the user is familiar with, and play there. For a unique production tone or expression, this might be the route a user takes.
We want to strike a good balance between convenience and complexity. It should be easy to select from a few relevant timbres, and significantly less work than configuring a synth to play the tuned notes. We should stop well short of writing a production synth.
Split from #9.
Dialogs can lose focus, because some operations like scrolling can trigger resize events, which redraws everything and loses UI state.
Enable loading of the configuration.
Consider also providing a 'reset' that is accessible in the UI, so that users are not left with an unusable app if there's an error loading the configuration.
Fix: For ED or generated repeating tunings, dynamically infer notes outside the explicitly defined range 0 .. Repeat ratio. Don't overwrite user-customized notes. Indicate inferred notes in the UI.
Most tunings are cyclic, defined explicitly over a Repeat ratio. Any notes outside the explicit range should be inferred for display and analysis.
Currently for ED tunings, we generate notes in the range -1 octaves .. +3 octaves, and export only the range -1 to , inclusive. While this works for most scenarios, it can be tricky to infer the provenance of individual notes (especially after editing), and we currently don't properly cover the display range for all sensible Repeat ratio values.
We already set tunings[].notes[].isDefined
, but need some analysis about the instantiation of implied 'ghost' notes for on-demand drawing and UI interactions, and whether they should persist in the tunings[].notes[]
array - and if so, do we use tunings[].notes[].isDefined
to distinguish explicit from implicit notes?
Whenever view parameters change (e.g. zoom bounds on the tuning rows, or analysis), expand/re-generate notes, setting .isDefined = false
. This needs a function to solve boundary frequency or count limits for scale generation.
As a user, I want to edit details for the last-touched note in a tuning strip, in a Note properties tab in the analysis section of the UI.
Follows #11
The note object contains many properties that must be kept consistent. For example, if the user edits the Frequency (Hz) of a note defined as a ratio, then several properties will need fixing up.
Mouseover title
text, while helpful to mouse users, cannot be viewed on a touch device.
As a mobile user, I want to see more precise details for analysis cells, where details are only displayed as mouseover text.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.