@arghotuning/arghotun-js v0.1.2
arghotun-js
IMPORTANT: This is a pre-release version of an upcoming open source library for working with musical tuning systems. It is NOT yet ready for external usage, and many of the linked websites and projects are not yet publicly available.
This is the canonical JavaScript (TypeScript) implementation for parsing &
serializing ArghoTuning files (*.arghotun.xml
). See the
Argho Tuning website or
official spec for more background.
It is published as the @arghotuning/arghotun-js NPM package including TypeScript type definition files, so it can be used by both TypeScript and plain JavaScript projects.
This library is designed only for use within a web browser; it does NOT currently support Node.js server/tool environments.
Usage
Add this dependency to your project, using your package manager. With NPM:
npm install @arghotuning/arghotun-js
To parse an ArghoTuning file from a string:
import {
ArghoTuningContext,
ArghoTuningParser,
defaultArghotunTranslations,
} from 'arghotun-js';
const arghoContext = new ArghoTuningContext({
translator: defaultArghotunTranslations(),
});
const parser = new ArghoTuningParser(arghoContext);
try {
const parseResult = parser.parseXmlString(inputXmlString);
if (parseResult.warnings.length >= 1) {
// Some file content wasn't understood. Display warnings[] to user that
// this tuning may not have been interpreted fully.
}
const tuning = parseResult.tuning;
} catch (err) {
// Input file was invalid; show err.message to user...
}
To output a human-readable ArghoTuning file to a string:
import { ArghoTuningXmlSerializer } from 'arghotun-js';
const serializer = new ArghoTuningSerializer(arghoContext);
const outputXmlString = serializer.serializeToString(tuning);
ArghoTuning Object Format
The provided types and methods correspond fairly closely to the elements and attributes of the XML format.
ArghoTuning
Initialize a new default N-tone equal temperament tuning, with root pitch of middle C (MIDI note 60), a master tune of A4 = 440.0 Hz, and a 1:1 key mapping:
const quarterToneTuning =
ArghoTuning.defaultNTET(arghoContext, 'My 24-TET Tuning', 24);
const defaultTuning =
ArghoTuning.default12TET(arghoContext, 'My 12-TET Tuning');
Access or update tuning-level metadata:
const name = tuning.getName();
tuning.setName('Updated Tuning Name');
const description = tuning.getDescription(); // Empty string ('') if none.
tuning.setDescription('Description of the tuning');
const accidentalPref = tuning.getDisplayAccidentalsAs();
if (accidentalPref === AccidentalDisplayPref.SHARPS) {
// Display any pitch names that require accidentals using sharps...
} else {
// Display any pitch names that require accidentals using flats...
}
tuning.setDisplayAccidentalsAs(AccidentalDisplayPref.FLATS);
Get the ArghoScale
and ArghoKeyMapping
(both are mutable):
const scale = tuning.getScale();
const mapping = tuning.getMapping();
Change the # of scale degrees or reset the scale and mapping. Note that add and remove are two separately named operations to emphasize their very different semantics:
// Add 3 additional degrees; each defaults to a 1:1 ratio (unison) measured from
// the root, without affecting any existing scale degrees. Newly added degrees
// are NOT mapped.
scale.addDegreesToEnd(3);
// Remove the last (by scale degree index) 3 degrees from the end of the scale.
// Any remaining scale degrees that were measured from the removed degrees will
// be updated to "skip over" the removed degrees, while preserving their
// existing tunings. Any input keys that were mapping to removed scale degrees
// are set to unmapped, leaving keySpan unchanged.
scale.removeDegreesFromEnd(3);
// Reset scale and mapping back to a 24-tone equal temperament scale and 1:1 key
// mapping, keeping the currently configured scale root and mapping root MIDI
// pitch. Also does not change other existing metadata (e.g. name, description).
scale.resetToDefaultNTET(24);
ArghoScale
Access metadata (note that changing the # of degrees must be performed on the
parent ArghoTuning
object):
const numDegrees = scale.getNumDegrees();
const octavesSpanned = scale.getOctavesSpanned(); // See spec for details.
Access or update the (sounding) scale root:
const root = scale.getRoot(); // Immutable.
scale.setRoot(updatedRoot); // Replaces old value.
Access or update an upper scale degree tuning or how it is measured. Remember
that indexes are 0-based (but should be displayed to the user as 1-based).
Changing measureFromIndex
preserves the sounding tuning:
const deg3 = scale.getUpperDegree(2); // Immutable.
const index = deg3.scaleDegreeIndex; // 2.
const measureFromIndex = deg3.measureFromIndex;
const interval = deg3.tunedInterval;
scale.setUpperDegreeTuning(2, updatedTunedInterval); // Replaces old value.
scale.setMeasureFromIndex(2, 5); // Measure 3rd scale degree from 6th degree.
Measure the interval between any two scale degrees (either can also be 0
for
the root):
const intervalFromDeg3ToRoot = scale.getIntervalFromSourceToDest(2, 0);
TunedInterval
Immutable value that measures an interval from a source pitch to a destination pitch, specified either in cents or as a frequency ratio. A negative cents value or a ratio less than 1 represents a downward interval.
Create a new immutable interval value:
const up123Dot45Cents = TunedInterval.fromCents(arghoContext, 123.45);
const downPure5th = TunedInterval.fromRatio(arghoContext, 2, 3);
Access cents or frequency ratio tuning:
if (interval.getSpecType() === TunedIntervalSpecType.CENTS) {
// User specified interval in cents...
} else {
// User specified interval as a frequency ratio...
}
// Regardless of how it was specified, all of these accessors will always
// yield values:
const cents = interval.getCents();
const numerator = interval.getRatioNumerator();
const denominator = interval.getDenominator(); // Defaults to 1.
Invert or add intervals:
const upPure5th = downPure5th.inverse(); // Returns new immutable value.
const upPure9th = upPure5th.plus(upPure5th); // (Ditto).
RootScaleDegree
Create default sounding root pitch (12-TET middle C, with master tune A4 = 440.0 Hz):
const defaultMiddleCRoot = RootScaleDegree.default(arghoContext);
Create custom souding root pitch, specified either by exact sounding frequency or relative to the closest 12-tone equal temperament tuned pitch. In either case, frequencies are still considered relative to the given master tuning (so if the master tuning is later changed, this sounding root pitch should be adjusted by the same amount):
const tuningA4Hz = 442.0;
const root234Dot5Hz = RootScaleDegree.exact(arghoContext, tuningA4Hz, 234.5);
const root12Dot3CentsBelowMiddleC =
RootScaleDegree.relative(arghoContext, tuningA4Hz, 60, -12.3);
Access values (all immutable):
const masterTuneA4Hz = root.getMasterTuneA4Hz();
if (root.getSpecType() === RootScaleDegreeSpecType.EXACT_FREQ) {
// User specified root as an exact sounding frequency in Hz...
} else {
// User specified root as a cents +/- offset relative to a 12-TET pitch...
}
// Regardless of how it was specified, all of these accessors will always
// yield values:
const rootFreqHz = root.getFreqHz();
const nearestMidiPitch = root.getNearestMidiPitch();
const centsFromNearestMidiPitch = root.getCentsFrom12TET();
ArghoKeyMapping
Access or update the # of keys in the mapping:
const keySpan = mapping.getKeySpan();
mapping.setKeySpan(24); // Any new keys start out unmapped.
Access or update mapped root MIDI pitch (which will sound the configured scale's root degree).
const mappingRootMidiPitch = mapping.getRootMidiPitch();
mapping.setRootMidiPitch(62); // Set to D above middle C. Doesn't affect scale.
Access or update mapped scale degrees:
const mappedDegreeIndex = mapping.getMappedScaleDegreeIndexOrNull(5);
if (mappedDegreeIndex == null) {
// Key isn't mapped to anything (don't produce any sound)...
} else {
// Mapped to a scale degree...
}
// When 6th key is played, play 3rd scale degree.
mapping.setMappedScaleDegreeIndex(5, 2);
// When 4th key is played, play the scale root.
mapping.setMappedScaleDegreeIndex(3, 0);
// When 3rd key is played, don't play any scale degree (unmap).
mapping.setMappedScaleDegree(2, null);
Translations and Internationalization (i18n)
Since the main user-visible strings this library produces are warnings and error messages, it is up to each client application project whether these need to be translated into languages other than English or not.
Currently, only English translations are included, but the library is set up to allow for simple translations to be provided for other locales.
Since client application projects likely will use different i18n frameworks, this library does not choose a specific framework to adopt; instead, it uses the lightweight interface from the simple-tr8n-js library, which can be used directly or easily adapted to work with any existing i18n framework.
See default-translations.ts for the default English strings.
License
All software released by the Arghonoon Project is available for use free of charge by open source and commercial projects alike, as per the Apache License 2.0.
RECOMMENDED: Please also contribute any fixes or improvements you make back to these official projects, thanks!
Related Projects
Canonical implementations are available in many programming languages (including
TypeScript/JavaScript, C++, Lua/KSP) for parsing & serialization, applying
these tunings once loaded (Argho Engine
projects), common UI components, and
more. See the list of official projects.
TODO: Update link once it exists.