Coder Social home page Coder Social logo

teoria's Introduction

Teoria.js

Teoria.js is a lightweight and fast JavaScript library for music theory, both Jazz and Classical. It aims at providing an intuitive programming interface for music software (such as Sheet Readers, Sheet Writers, MIDI Players etc.).

Features

  • A note object (teoria.Note), which understands alterations, octaves, key number, frequency and etc. and Helmholtz notation

  • A chord object (teoria.Chord), which understands everything from simple major/minor chords to advanced Jazz chords (Ab#5b9, F(#11) and such)

  • A scale object (teoria.Scale), The scale object is a powerful presentation of a scale, which supports quite a few handy methods. A scale can either be constructed from the predefined scales, which by default contains the 7 modes (Ionian, Dorian, Phrygian etc.) a major and minor pentatonic and the harmonic chromatic scale or from an arbitrary array of intervals. The scale object also supports solfège, which makes it perfect for tutorials on sight-reading.

  • An interval object (teoria.Interval), which makes it easy to find the interval between two notes, or find a note that is a given interval from a note. There's also support for counting the interval span in semitones and inverting the interval.

Usage

$ npm install teoria

Can be used with both Node and Browserify/webpack/etc.

... or less preferable

Include the bundled build file, teoria.js from this repository, directly:

<script src="path/to/teoria.js"></script>

Syntax

This is just a short introduction to what teoria-code looks like, for a technical library reference, look further down this document.

// Create notes:
var a4 = teoria.note('a4');       // Scientific notation
var g5 = teoria.note("g''");      // Helmholtz notation
var c3 = teoria.note.fromKey(28); // From a piano key number

// Find and create notes based on intervals
teoria.interval(a4, g5);    // Returns a Interval object representing a minor seventh
teoria.interval(a4, 'M6');  // Returns a Note representing F#5
a4.interval('m3');          // Returns a Note representing C#4
a4.interval(g5);            // Returns a Interval object representing a minor seventh
a4.interval(teoria.note('bb5')).invert(); // Returns a Interval representing a major seventh

// Create scales, based on notes.
a4.scale('mixolydian').simple();  // Returns: ["a", "b", "c#", "d", "e", "f#", "g"]
a4.scale('aeolian').simple();     // Returns: ["a", "b", "c", "d", "e", "f", "g"]
g5.scale('ionian').simple();      // Returns: ["g", "a", "b", "c", "d", "e", "f#"]
g5.scale('dorian');               // Returns a Scale object

// Create chords with the powerful chord parser
a4.chord('sus2').name;    // Returns the name of the chord: 'Asus2'
c3.chord('m').name;       // Returns 'Cm'
teoria.chord('Ab#5b9');   // Returns a Chord object, representing a Ab#5b9 chord
g5.chord('dim');          // Returns a Chord object, representing a Gdim chord

// Calculate note frequencies or find the note corresponding to a frequency
teoria.note.fromFrequency(467); // Returns: {'note':{...},'cents':3.102831} -> A4# a little out of tune.
a4.fq(); // Outputs 440
g5.fq(); // Outputs 783.9908719634985

// teoria allows for crazy chaining:
teoria.note('a')    // Create a note, A3
  .scale('lydian')  // Create a lydian scale with that note as root (A lydian)
  .interval('M2')   // Transpose the whole scale a major second up (B lydian)
  .get('third')     // Get the third note of the scale (D#4)
  .chord('maj9')    // Create a maj9 chord with that note as root (D#maj9)
  .toString();      // Make a string representation: 'D#maj9'

Documentation

teoria.note (name | coord[, duration])

name - The name argument is the note name as a string. The note can both be expressed in scientific and Helmholtz notation. Some examples of valid note names: Eb4, C#,,, C4, d#'', Ab2

coord - If the first argument isn't a string, but a coord array, it will instantiate a Note instance.

duration - The duration argument is an optional object argument. The object has two also optional parameters:

  • value - A number corresponding to the value of the duration, such that: 1 = whole, 2 = half (minim), 4 = quarter, 8 = eight

  • dots - The number of dots attached to the note. Defaults to 0.

teoria.note.fromKey(key)

A static method that returns an instance of Note set to the note at the given piano key, where A0 is key number 1. See Wikipedia's piano key article for more information.

teoria.note.fromFrequency(fq)

A static method returns an object containing two elements:

note - A Note which corresponds to the closest note with the given frequency

cents - A number value of how many cents the note is out of tune

teoria.note.fromMIDI(note)

  • Returns an instance of Note set to the corresponding MIDI note value.

note - A number ranging from 0-127 representing a MIDI note value

teoria.note.fromString(note)

  • Returns an instance of Note representing the note name

note - The name argument is the note name as a string. The note can both be expressed in scientific and Helmholtz notation. Some examples of valid note names: Eb4, C#,,, C4, d#'', Ab2

Note.name()

  • The name of the note, in lowercase letter (only the name, not the accidental signs)

Note.octave()

  • The numeric value of the octave of the note

Note.duration

  • The duration object as described in the constructor for Note

Note.accidental()

  • Returns the string symbolic of the accidental sign (x, #, b or bb)

Note.accidentalValue()

  • Returns the numeric value (mostly used internally) of the sign: x = 2, # = 1, b = -1, bb = -2

Note#key([whitenotes])

  • Returns the piano key number. E.g. A4 would return 49

whitenotes - If this parameter is set to true only the white keys will be counted when finding the key number. This is mostly for internal use.

Note#midi()

  • Returns a number ranging from 0-127 representing a MIDI note value

Note#fq([concertPitch])

  • Calculates and returns the frequency of the note.

concertPitch - If supplied this number will be used instead of the normal concert pitch which is 440hz. This is useful for some classical music.

Note#chroma()

  • Returns the pitch class (index) of the note.

This allows for easy enharmonic checking:

teoria.note('e').chroma() === teoria.note('fb').chroma();

The chroma number is ranging from pitch class C which is 0 to 11 which is B

Note#scale(scaleName)

  • Returns an instance of Scale, with the tonic/root set to this note.

scaleName - The name of the scale to be returned. 'minor', 'chromatic', 'ionian' and others are valid scale names.

Note#interval(interval)

  • A sugar function for calling teoria.interval(note, interval);

Look at the documentation for teoria.interval

Note#transpose(interval)

  • Like the #interval method, but changes this note, instead of returning a new

Note#chord([name])

  • Returns an instance of Chord, with root note set to this note

name - The name attribute is the last part of the chord symbol. Examples: 'm7', '#5b9', 'major'. If the name parameter isn't set, a standard major chord will be returned.

Note#helmholtz()

  • Returns the note name formatted in Helmholtz notation.

Example: teoria.note('A5').helmholtz() -> "a''"

Note#scientific()

  • Returns the note name formatted in scientific notation.

Example: teoria.note("ab'").scientific() -> "Ab4"

Note#enharmonics(oneAccidental)

  • Returns all notes that are enharmonic with the note

oneAccidental - Boolean, if set to true, only enharmonic notes with one accidental is returned. E.g. results such as 'eb' and 'c#' but not 'ebb' and 'cx'

teoria.note('c').enharmonics().toString();
// -> 'dbb, b#'

teoria.note('c').enharmonics(true).toString();
// -> 'b#'

Note#durationInSeconds(bpm, beatUnit)

  • Returns the duration of the note, given a tempo (in bpm) and a beat unit (the lower numeral of the time signature)

Note#solfege(scale, showOctaves)

  • Returns the solfege step in the given scale context

scale - An instance of Scale, which is the context of the solfege step measuring

showOctaves - A boolean. If set to true, a "Helmholtz-like" notation will be used if there's bigger intervals than an octave

Note#durationName()

  • Returns the duration name.

Examples: teoria.note('A', 8).durationName() -> 'eighth', teoria.note('C', 16).durationName() -> 'sixteenth'

Note#scaleDegree(scale)

  • Returns this note's degree in a given scale (Scale). For example a D in a C major scale will return 2 as it is the second degree of that scale. If however the note isn't a part of the scale, the degree returned will be 0, meaning that the degree doesn't exist. This allows this method to be both a scale degree index finder and an "isNoteInScale" method.

scale - An instance of Scale which is the context of the degree measuring

Note#toString([dontShow])

  • Usability function for returning the note as a string

dontShow - If set to true the octave will not be included in the returned string.

Chord(root, chord)

  • A chord class with a lot of functionality to alter and analyze the chord.

root - A Note instance which is to be the root of the chord

chord - A string containing the chord symbol. This can be anything from simple chords, to super-advanced jazz chords thanks to the detailed and robust chord parser engine. Example values: 'm', 'm7', '#5b9', '9sus4 and '#11b5#9'

teoria.chord(name || note[, octave || symbol])

  • A simple function for getting the notes, no matter the octave, in a chord

name - A string containing the full chord symbol, with note name. Examples: 'Ab7', 'F#(#11b5)'

note - Instead of supplying a string containing the full chord symbol, one can pass a Note object instead. The note will be considered root in the new chord object

octave - If the first argument of the function is a chord name (typeof "string"), then the second argument is an optional octave number (typeof "number") of the root.

symbol - A string containing the chord symbol (excluding the note name)

Chord.name

  • Holds the full chord symbol, inclusive the root name.

Chord.root

  • Holds the Note that is the root of the chord.

Chord#notes()

  • Returns an array of Notes that the chord consists of.

Chord#simple()

  • Returns an Array of only the notes' names, not the full Note objects.

Chord#bass()

  • Returns the bass note of the chord (The note voiced the lowest)

Chord#voicing([voicing])

  • Works both as a setter and getter. If no parameter is supplied the current voicing is returned as an array of Intervals

voicing - An optional array of intervals in simple-format that represents the current voicing of the chord.

Here's an example:

var bbmaj = teoria.chord('Bbmaj7');
// Default voicing:
bbmaj.voicing();  // #-> ['P1', 'M3', 'P5', 'M7'];
bbmaj.notes();    // #-> ['bb', 'd', 'f', 'a'];

// New voicing
bbmaj.voicing(['P1', 'P5', 'M7', 'M10']);
bbmaj.notes();    // #-> ['bb', 'f', 'a', 'd'];

NB: Note that above returned results are pseudo-results, as they will be returned wrapped in Interval and Note objects.

Chord#quality()

  • Returns a string which holds the quality of the chord, 'major', 'minor', 'augmented', 'diminished', 'half-diminished', 'dominant' or undefined

Chord#get(interval)

  • Returns the note at a given interval in the chord, if it exists.

interval - A string name of an interval, for example 'third', 'fifth', 'ninth'.

Chord#dominant([additional])

  • Returns the naïvely chosen dominant which is a perfect fifth away.

additional - Additional chord extension, for example: 'b9' or '#5'

Chord#subdominant([additional])

  • Returns the naïvely chosen subdominant which is a perfect fourth away.

additional - Like the dominant's.

Chord#parallel([additional])

  • Returns the parallel chord for major and minor triads

additional - Like the dominant's

Chord#chordType()

  • Returns the type of the chord: 'dyad', 'triad', 'trichord', 'tetrad' or 'unknown'.

Chord#interval(interval)

  • Returns the same chord, a interval away

Chord#transpose(interval)

  • Like the #interval method, except it's this chord that gets changed instead of returning a new chord.

Chord#toString()

  • Simple usability function which is an alias for Chord.name

Scale(tonic, scale)

  • The teoria representation of a scale, with a given tonic.

tonic - A Note which is to be the tonic of the scale

scale - Can either be a name of a scale (string), or an array of absolute intervals that defines the scale. The scales supported by default are:

  • major
  • minor
  • ionian (Alias for major)
  • dorian
  • phrygian
  • lydian
  • mixolydian
  • aeolian (Alias for minor)
  • locrian
  • majorpentatonic
  • minorpentatonic
  • chromatic
  • harmonicchromatic (Alias for chromatic)
  • blues
  • doubleharmonic
  • flamenco
  • harmonicminor
  • melodicminor
  • wholetone

teoria.scale(tonic, scale)

  • Sugar function for constructing a new Scale object

teoria.Scale.KNOWN_SCALES

  • An array of all the scale ID's that comes with teoria

Scale.name

  • The name of the scale (if available). Type string or undefined

Scale.tonic

  • The Note which is the scale's tonic

Scale#notes()

  • Returns an array of Notes which is the scale's notes

Scale#simple()

  • Returns an Array of only the notes' names, not the full Note objects.

Scale#type()

  • Returns the type of the scale, depending on the number of notes. A scale of length x gives y:
  • 2 gives 'ditonic'
  • 3 gives 'tritonic'
  • 4 gives 'tetratonic'
  • 5 gives 'pentatonic'
  • 6 gives 'hexatonic',
  • 7 gives 'heptatonic',
  • 8 gives 'octatonic'

Scale#get(index)

  • Returns the note at the given scale index

index - Can be a number referring to the scale step, or the name (string) of the scale step. E.g. 'first', 'second', 'fourth', 'seventh'.

Scale#solfege(index, showOctaves)

  • Returns the solfege name of the given scale step

index Same as Scale#get

showOctaves - A boolean meaning the same as showOctaves in Note#solfege

teoria.interval(from, to)

  • A sugar function for the #from and #between methods of the same namespace and for creating Interval objects.

teoria.interval(string: from)

  • A sugar method for the Interval.toCoord function

teoria.interval(Note: from, string: to)

  • A sugar method for the Interval.from function

teoria.interval(Note: from, Interval: to)

  • Like above, but with a Interval instead of a string representation of the interval

teoria.interval(Note: from, Note: to)

  • A sugar method for the Interval.between function
teoria.interval.from -> Interval.from
teoria.interval.between -> Interval.between
teoria.interval.invert -> Interval.invert
teoria.interval.toCoord -> Interval.toCoord

Interval(coord)

  • A representation of a music interval

Interval.toCoord(simpleInterval)

  • Returns a Interval representing the interval expressed in string form.

Interval.from(from, to)

  • Returns a note which is a given interval away from a root note.

from - The Note which is the root of the measuring

to - A Interval

Interval.between(from, to)

  • Returns an interval object which represents the interval between two notes.

from and to are two Notes which are the notes that the interval is measured from. For example if 'a' and 'c' are given, the resulting interval object would represent a minor third.

Interval.between(teoria.note("a"), teoria.note("c'")) -> teoria.interval('m3')

Interval.invert(simpleInterval)

  • Returns the inversion of the interval provided

simpleInterval - An interval represented in simple string form. Examples:

  • 'm3' = minor third
  • 'P4' = perfect fourth
  • 'A4' = augmented fifth
  • 'd7' = diminished seventh
  • 'M6' = major sixth.

'm' = minor, 'M' = major, 'A' = augmented and 'd' = diminished

The number may be prefixed with a - to signify that its direction is down. E.g.:

m-3 means a descending minor third, and P-5 means a descending perfect fifth.

Interval.coord

  • The interval representation of the interval

Interval.number()

  • The interval number (A ninth = 9, A seventh = 7, fifteenth = 15)

Interval.value()

  • The value of the interval - That is a ninth = 9, but a downwards ninth is = -9

Interval.toString()

  • Returns the simpleInterval representation of the interval. E.g. 'P5', 'M3', 'A9', etc.

Interval.base()

  • Returns the name of the simple interval (not compound)

Interval.type()

  • Returns the type of array, either 'perfect' (1, 4, 5, 8) or 'minor' (2, 3, 6, 7)

Interval.quality([verbose])

  • The quality of the interval ('dd', 'd' 'm', 'P', 'M', 'A' or 'AA')

verbose is set to a truish value, then long quality names are returned: 'doubly diminished', 'diminished', 'minor', etc.

Interval.direction([dir])

  • The direction of the interval

dir - If supplied, then the interval's direction is to the newDirection which is either 'up' or 'down'

Interval#semitones()

  • Returns the number of semitones the interval span.

Interval#simple([ignoreDirection])

  • Returns the simple part of the interval as a Interval. Example:

ignoreDirection - An optional boolean that, if set to true, returns the "direction-agnostic" interval. That is the interval with a positive number.

teoria.interval('M17').simple();    // #-> 'M3'
teoria.interval('m23').simple();    // #-> 'm2'
teoria.interval('P5').simple();     // #-> 'P5'
teoria.interval('P-4').simple();    // #-> 'P-4'

// With ignoreDirection = true
teoria.interval('M3').simple(true);     // #->'M3'
teoria.interval('m-10').simple(true);   // #-> 'm3'

NB: Note that above returned results are pseudo-results, as they will be returned wrapped in Interval objects.

Interval#octaves()

  • Returns the number of compound intervals

Interval#isCompound()

  • Returns a boolean value, showing if the interval is a compound interval

Interval#add(interval)

  • Adds the interval to this interval, and returns a Interval representing the result of the addition

Interval#equal(interval)

  • Returns true if the supplied interval is equal to this interval

Interval#greater(interval)

  • Returns true if the supplied interval is greater than this interval

Interval#smaller(interval)

  • Returns true if the supplied interval is smaller than this interval

Interval#invert()

  • Returns the inverted interval as a Interval

Interval#qualityValue() - internal

  • Returns the relative to default, value of the quality. E.g. a teoria.interval('M6'), will have a relative quality value of 1, as all the intervals defaults to minor and perfect respectively.

teoria's People

Contributors

alexesdev avatar benmurrell avatar crotwell avatar emanchado avatar garrettmaring avatar jackca avatar jaylupus avatar joscha avatar lukehorvat avatar matthewconstantine avatar mjhasbach avatar nick-thompson avatar panarch avatar peterkhayes avatar saebekassebil avatar seanyeh avatar urbanmichal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

teoria's Issues

MusicJSON Support?

Hopefully this isn't just because I missed something obvious, but is there any support in teoria for the MusicJSON format (specifically, the output from your musicjson library at https://github.com/saebekassebil/musicjson)? I'm playing around with some analytic stuff, and using the musicjson library to get MusicXML files in something I can work with; teoria seems like it might handle a bit more of the theory-related stuff for what I'm doing, but I'd rather not manually create the teoria objects if there's a parser for your json stuff somewhere.

Thanks!

Correct Helmholtz notation

The implementation of the Helmholtz notation is pt. erroneous. Helmholtz notation does not prefix commas with octaves lower than the great octave (contra octave, subcontra octave, etc). The commas are suffixed, though the note name is still uppercase.

// Expected to throw:
teoria.note(',C') === teoria.note('C1'); 

// Actual result
true

chromatic scales?

Hello,

Is there any way to make one with teoria? I tried just entering 12 m2s into the scales array, but it only works for the first 4 notes of most scales... at that point teoria has to make a triple flat and balks.

For example, for a chromatic scale with a C4 root note I get:

C4, Db4, Ebb4, Fbb4, G4, Ab4...

There's a big break between Fbb4 and G4 obviously. Thanks for any thoughts! - Charlie

Perfect octave and large intervals

Hey there @saebekassebil

I'm missing something with perfect octaves

note("c4").inverval("P8") .. the result should be c5 but I'm geting c4.

I also need bigger intervas in my app:

note("c4").interval("P-16") should be c2
note ("d4").interval("M-17") should be c2

any ideas ?

No support for double diminished and double augmented intervals

In theory there exists the notion of "double augmented" and "double diminished" intervals, which aren't implemented in teoria. The lack of support for these creates some limitations to the interval structure. For example, this is impossible:

teoria.note('db').interval(teoria.note('d#')) -> Error: Invalid interval quality
teoria.note('c#').interval(teoria.note('cb')) -> Error: Invalid interval quality
teoria.note('bb4').interval(teoria.note('b#5')) -> Error: Invalid interval quality

These examples are (theoretically) expected to return: 'double diminished unison', 'double diminished unison' and 'double augmented octave'.

The reason that these haven't been implemented was the notes' limits in accidentals. It seems only reasonable to support the double sharp: x/## and the double flat bb. If these double diminished/augmented intervals will be implemented it will probably lead to many more "Invalid Notes". Any note with a sharp (A#, C#, D#, etc), will not be capable of jumping in double augmented intervals, just as any note with a flat (Eb, Fb, Ab, etc) won't be able jump in double diminished intervals.

The reason above is why double augmented/diminished intervals aren't implemented, but maybe it's the correct theoretical way to go, as it must be the developers responsibility not to do anything theoretical incorrect, or else be prepared to catch an error.

Tone value in TeoriaNote

Hi,

thank you very much for sharing this great library! I found it just before starting one on my own, but teoria is already far beyond from what I've thought of initially...

I have a little feature proposition:
It would be useful to append another attribute/method to the TeoriaNote object. Among others you have already the possibility to get the octave, the name or even the corresponding piano key of a note, but what's missing from a music computing perspective, is the "tone" value, at best a integer from 0 to 11 of the chromatic scale, or a standard form of the scale to be able to map TeoriaNotes to other libraries.
Else I have to check for enharmonics when I want to know if a TeoriaNote object is, let's say a "C#".

I've also read the your conversation with @gregjopa about different musical temperaments, maybe if would be easier, adding a layer of abstraction between labeling and computing the notes. In other words: If a note label depends on the tone, octave, current scale, and tuning temperament, maybe it shouldn't be an attribute of the note object.

I hope this makes sense, please correct me if not :)

Thanks again and best wishes,
Felix

Add distribution file to repository

I think it would be nice to have a pre-rendered dist/teoria.js ready for web environments inside the repository and in sync with the source.

Transposing chords by a number of semitones

I'm creating a simple ukulele composition tool and need a way to transpose chords by a numerical offset in semitones (this is common in lots of tablature sites/apps).

In order to transpose a Chord, you must pass an Interval object to the .transpose or .interval method, but I don't know how to create such an Interval that correctly represents the operation I'm trying to achieve.

I'm not very familiar with music theory, but I determined that I can achieve this with a lookup table of interval names that achieve what I want:

var INTERVAL_NAMES = [
  'P1',
  'm2',
  'M2',
  'm3',
  'M3',
  'P4',
  'A4',
  'P5',
  'm6',
  'M6',
  'm7',
  'M7',
  'P8',
];

// Handles modulo correctly for negative numbers:
function mod(n, m) {
  return ((n % m) + m) % m;
}

function intervalFromSemitoneOffset (n) {
  return teoria.interval(INTERVAL_NAMES[mod(n, 12)]);
}

This seems to work, but feels sloppy. What would you suggest? Personally, I'd love it if .transpose/.interval could simply take an integer value, but I'm probably misunderstanding something about how intervals work.

M for major

Teoria currently interprets "CM7" as a "C7" (dominant 7th) chord, when it should interpret it as a "Cmaj7".

A capitalized "M" in chord names is synonymous with "Maj". While using "M" is bad practice (due to confusion with lower-case "m", especially with certain fonts), it is universally recognized as meaning "major", and unfortunately this notation is common enough in books, sheet music, and chord charts, that teoria should probably support it.

Note: I know that teoria supports "M" as a standalone 7th qualifier, and it should continue to support this. e.g. don't change the behaviour of "CmM7" (C minor chord with major 7th) or "C+M7" (C augmented chord with major 7th). But in addition to that, "M" should be equivalent to "Maj" when used for the chord quality definitions themselves. In other words, when "M" immediately follows the note name, it should act the same as if it were "Maj" following the note name. Examples: "CM13" == "Cmaj13", "BbM7#11" == "BbMaj7#11".

Similar to this, I would also recommend allowing "Ma" as another "maj" equivalent, and "Mi" for "min". Example usages:

Reharmonization library for Teoria?

I'd like to create a reharmonization library that depends on teoria. Would you be interested in something like this? I see the library providing methods for calculating reharmonizations using all of the standard techniques and then some. I see that Jason Storey created teoria-chord-progression as a standalone package. I'm thinking that would be a good approach for a library like this as well?

Fix descending intervals

teoria.interval.from doesn't respect the direction of an interval:

Actual result from trying to find the minor third below C4:

teoria.interval.from(teoria.note('C4'), teoria.interval('m3', 'down')) == teoria.note('e3')

Expected behaviour is, to return teoria.note('A3')

Settings direction of an interval to what it already is, fails

When using the TeoriaInterval#direction(dir) is a setter, the direction is toggled no matter what you set the direction to. That is, it is not idempotent which it ought to be.

Example:

var m3 = teoria.note('a3').interval(teoria.note('c4'));
console.log(m3.direction()); // -> 'up'
m3.direction('up');
console.log(m3.direction()); // -> 'down' ?!

Build Example Fails with more Scales

Not sure if this is an issue with my environment or not, I definitely was able to build this when I had cloned it on a different machine, but I get this error when trying to build (this is the example given in the readme).

image

Also, is there a way to just say I want ALL the scales? Not sure if there is a shorthand for that or not, but it would be useful...

Regardless, I might just for now add them manually to the jake file if I can, but I figured I would shoot this out.

Repeatedly adding intervals to a note results in the note's coord going out of bounds

Here's a snippet that brings the problem to light:

var note = teoria.note( 'c2' );
for( var i = 0; i < 12; i++ ) {
    console.log( "Note: " + note.toString() + ", coord: " + note.coord + ", semitones from c2: " + teoria.note( 'c2' ).interval( note ).semitones() );
    note = note.interval( 'm2' );
}

Output:

Note: c2, coord: -1,-3, semitones from c2: 0 
Note: db2, coord: 2,-8, semitones from c2: 1 
Note: ebb2, coord: 5,-13, semitones from c2: 2 
Note: fbb2, coord: 8,-18, semitones from c2: 3 
Note: gundefined2, coord: 11,-23, semitones from c2: 4 
Note: aundefined2, coord: 14,-28, semitones from c2: 5 
Note: bundefined2, coord: 17,-33, semitones from c2: 6 
Note: cundefined3, coord: 20,-38, semitones from c2: 7 
Note: dundefined3, coord: 23,-43, semitones from c2: 8 
Note: eundefined3, coord: 26,-48, semitones from c2: 9 
Note: fundefined3, coord: 29,-53, semitones from c2: 10 
Note: gundefined3, coord: 32,-58, semitones from c2: 11 

As you can see, the interval is the correct number of semitones, but the coord of the resulting note differs significantly from what we would get if we just asked for it via teoria.note(). The out of bound coords breaks the calculation of accidentals, octaves, and note names - the last note logged should be b2.

I'm not sure what the coord represents exactly, so this is difficult for me to fix.

Bower install

Hey

I'm trying to use teoria with bower. After "bower install teoria" I can see the library and sources but there is no "teoria.js" file that I can use directly like other libraries.

Correct the behavior of TeoriaNote#solfege(scale, true)

Right now the implementation of a non-octave-agnostic solfege method in TeoriaNote is faulty. The implementation should assign strokes (commas), to the syllable when the note is located under the scale's tonic.

Thus C#4 in a D4 major scale should return ti, where as it now just return ti. If this is not implemented there would be no way to determine the octave of a solfege syllable relative to a tonic, as there would be an identical syllable in both octave directions.

This is concerned with the later discussion in issue #6

Getting chord from notes.

Would it be possible to have a feature where I could build a "collection" of notes.

notes = ['C4', 'D#4', 'F' ]

And then get a chord name (if "valid")

getChord( notes ); #=> 'Cm'

Or does that not make any sense at all?

Get The Note

Hello

I am looking for a way to get the note only from teoria note:

v = teoria.note('Bb4');

v.something() will get me 'Bb';

Any ideas ?

Thanks
I have released a first demo of my project using Teoria, Thank You!
http://www.windsoloist.com

Improve chord build errors

Hi,

I have a work in progress where user can type their one chord, and this goes to the chord builder. It's very straightforward because the chord parser does the analysis of the user input for me.

My goal today it to display error to the user in his language. I could translate the error message that the library return, but it is definitively a bad solution (and moreover the error returned contains some variables).

I have though about 2 obvious way to fix it (and both can be implemented together).

  • 1 - add a translation to the library that we can pass globaly to teoria at the runtime, and we can return already translated messages to the user
  • 2 - throw a different exception for each error : this would be very helpful. It will allow to know what is going wrong before telling the user it's wrong. Then we can adapt the action we want the user to achieve to fix it.

obviously, we can mix both solution and throw different exception with translated message.

chord only creates in a single octave

Great library! One concern I have is that the chord function doesn't handle octaves correctly.

For example, teoria.chord("Cm7") yields all notes with an octave of three. The Bb should have an octave of four assuming you build up from the root (which seems like a natural assumption to me).

Am I doing something wrong? If it just doesn't function that way I'll try and make a patch, but let me know if I'm missing something.

Thank you for this! - Charlie

missing note.midi()

Since we have teoria.note.fromKey(), note.key() and teoria.note.fromMIDI() it would be nice to have note.midi(). I can PR If you think that's a good idea.

Feature request: a function to determine whether a note is a scale degree

It would be cool if there was a way to tell which notes comprise a given scale, and which ones do not. For example, the notes that comprise Eb major are Eb, F, G, Ab, Bb, C, and D. Given that, it would be cool if one could do something like this (I don't know if isScaleDegree is the right name for the function, but you get the idea):

s = teoria.scale("eb4", "major");
teoria.note("eb4").isScaleDegree(s); //returns "true"
teoria.note("f2").isScaleDegree(s); //returns "true"
teoria.note("g3").isScaleDegree(s); //returns "true"
teoria.note("g4").isScaleDegree(s); //returns "true"
teoria.note("g5").isScaleDegree(s); //returns "true"
teoria.note("a4").isScaleDegree(s); //returns "false"
teoria.note("b5").isScaleDegree(s); //returns "false"
teoria.note("d#4").isScaleDegree(s); //returns "false"

(Observe how the function would be octave-agnostic.)

I think this would be helpful for those rendering music notation and using Teoria as the music theory framework. You could basically iterate through each note, and only render an accidental on the stave when the isScaleDegree function returns false (because any note that returns true would be covered by the key signature).

I noticed you already have teoria.scale.scales, which is used internally. So I think the isScaleDegree function would be as simple as getting the interval from the scale tonic to the specified note (ignoring the octave difference somehow), and then returning a boolean indicating whether that interval exists in the scale's array in teoria.scale.scales.

Alternatively, instead of a boolean you could return the number of the scale degree (e.g. for C major, C would return 1, D would return 2, E would return 3, C# would return undefined, etc.), which you could do by simply returning the index of the interval in the scale's array in teoria.scale.scales. The function would need a different name though, like getScaleDegree or just scaleDegree.

Better error message for undefined intervals

Right now the following expression:

teoria.note('e').interval(teoria.note('fbb'))

throws

TypeError: Cannot read property 'quality' of undefined

it should be a more telling message. The source of this error resides in the teoria.interval.between function.

Interval direction is incorrect on d-2

A downwards diminished second (which is 0 semitones and enharmonic to a unison), returns its direction as 'up':

teoria.interval('d-2').direction() === 'up'

Although this is far out, this should be corrected, so that the direction of an interval always sticks no matter how silly it is.

The current usage of #semitones() to measure direction is flawed and should be fixed.

Cannot determine interval between two identical notes with different accidental signs

Second issue for the day. :) It's related to issue 6.

Say I do the following:

teoria.interval.between(teoria.note("db4"), teoria.note("c#4"))

This raises a Cannot read property 'quality' of undefined error, because the two notes are the same. For whatever reason, it cannot lookup the interval.

Oddly, no error is generated when I do:

teoria.interval.between(teoria.note("c#4"), teoria.note("c#4")) //returns "P1"

It would be nice to have this fixed, because for my particular use case I am reading some notes from file and generating others programmatically, so I don't really have control over whether accidentals assume the "b" or "#" sign.

Adding two intervals

I'm looking for the best way to add two intervals.

m3 + M2 = P4
P5 + m-6 = M-2

Any ideas for the right way to do it ?

Simplifying extensibility mechanisms.

I'm not saying you should necessarily expose your classes publicly, this is just one approach (see bellow) but as food for thought:

Last evening I wanted to extend the Note object with a toUTF8() method (which would replace #,b etc from the simple name with real utf8 musical entities ).

  1. I could not extend the prototype, for it's not exposed.
  2. I would have had to monkey patch all teoria methods (chord, note, scale) etc and do itterations on collections to find notes adding the method to each note instance.

It would have been a cinch had I been able to do:

teoria.TeoriaNote.prototype.toUTF8 = function(){ return this.name.replace /...../ }

Or maybe I'm missing something? In either case, I think planning extensibility will give you more control on what you decide to bring into the core and what you decide doesn't go there without preventing others to do so.

Possible bug: TeoriaNote#solfege is not octave-independent?

Firstly, this is how I expected TeoriaNote#solfege to work:

s = teoria.scale("c", "major");
teoria.note("a#3").solfege(s); //returns "li"
teoria.note("a#4").solfege(s); //returns "li"
teoria.note("a#5").solfege(s); //returns "li"
s = teoria.scale("c4", "major");
teoria.note("a#3").solfege(s); //returns "li"
teoria.note("a#4").solfege(s); //returns "li"
teoria.note("a#5").solfege(s); //returns "li"

However, this is how it actually works:

s = teoria.scale("c", "major");
teoria.note("a#3").solfege(s); //returns "li"
teoria.note("a#4").solfege(s); //returns undefined
teoria.note("a#5").solfege(s); //error: 'Interval is bigger than an augmented fifteenth'
s = teoria.scale("c4", "major");
teoria.note("a#3").solfege(s); //returns undefined
teoria.note("a#4").solfege(s); //returns "li"
teoria.note("a#5").solfege(s); //returns undefined

Just wondering if this behaviour is by design. If so, care to clarify your rationale? It's not a huge deal, but it does force the developer to be extra conscious of the scale tonic's octave.

Normalizing a note

Somtimes musical operations on note and interval gets notes like B# or Fb. I needed to simplify that to c and e, I wrote this function. Any other way to do that ?

teoria.normalizeNote = function(n){

var acc = n.accidental();

if (acc === '#') {
    var name =  n.name();
    if (name === 'b'){
        return teoria.note('c' + (n.octave() + 1));
    }
    if (name === 'e'){
        return teoria.note('f' + (n.octave()));
    }
}

if (acc === 'b') {
    var name =  n.name();
    if (name === 'c'){
        return teoria.note('b' + (n.octave() - 1));
    }
    if (name === 'f'){
        return teoria.note('e' + (n.octave()));
    }
}
return n;

I guess there should be a function for the general case Gbb=F

Inversions

What is the easiest way of creating a chord inversion? i.e moving the root to the top
Am (A, C, E) -> (C, E, A)
Would it be useful to have a chord function for creating these?

Awesome library!

Thanks

Linting

With the new linting system in place, it's obvious that it needs some tweaking, and a lot of the code needs to be linted.

jake lint output:

 [INFO] 6 files linted, 1 successfully, 5 with errors!
 src/core.js: line 339, col 21, Expected '===' and instead saw '=='.
 src/core.js: line 455, col 59, Expected '===' and instead saw '=='.
 src/core.js: line 6, col 45, 'exports' is not defined.
 src/core.js: line 57, col 17, 'kDurations' is defined but never used.
 src/core.js: line 104, col 21, 'kIntervalIndex' is defined but never used.
 src/core.js: line 126, col 19, 'kQualityTemp' is defined but never used.
 src/core.js: line 136, col 22, 'kValidQualities' is defined but never used.
 src/core.js: line 155, col 24, 'kQualityInversion' is defined but never used.
 src/core.js: line 173, col 14, 'kChords' is defined but never used.
 src/core.js: line 183, col 18, 'kChordShort' is defined but never used.
 src/core.js: line 199, col 23, 'kAccidentalValue' is defined but never used.
 src/core.js: line 206, col 18, 'kStepNumber' is defined but never used.
 src/core.js: line 228, col 23, 'kIntervalSolfege' is defined but never used.
 src/core.js: line 281, col 15, 'pad' is defined but never used.
 src/core.js: line 360, col 56, 'intervalName' is defined but never used.
 src/core.js: line 403, col 47, 'alterations' is defined but never used.
 src/note.js: line 165, col 38, Expected '===' and instead saw '=='.
 src/note.js: line 38, col 25, 'kAccidentalValue' is not defined.
 src/note.js: line 52, col 25, 'kAccidentalValue' is not defined.
 src/note.js: line 92, col 29, 'kNotes' is not defined.
 src/note.js: line 95, col 19, 'kNotes' is not defined.
 src/note.js: line 141, col 18, 'kChordShort' is not defined.
 src/note.js: line 142, col 15, 'kChordShort' is not defined.
 src/note.js: line 157, col 5, 'pad' is not defined.
 src/note.js: line 158, col 12, 'pad' is not defined.
 src/note.js: line 179, col 46, 'kAccidentalSign' is not defined.
 src/note.js: line 185, col 46, 'kAccidentalSign' is not defined.
 src/note.js: line 208, col 15, 'kIntervalSolfege' is not defined.
 src/note.js: line 209, col 28, 'pad' is not defined.
 src/note.js: line 217, col 12, 'kDurations' is not defined.
 src/interval.js: line 2, col 53, Expected '===' and instead saw '=='.
 src/interval.js: line 5, col 28, 'kIntervals' is not defined.
 src/interval.js: line 8, col 20, 'kValidQualities' is not defined.
 src/interval.js: line 27, col 12, 'kQualityTemp' is not defined.
 src/interval.js: line 31, col 12, 'kQualityTemp' is not defined.
 src/interval.js: line 45, col 31, 'kQualityInversion' is not defined.
 src/interval.js: line 51, col 12, 'kValidQualities' is not defined.
 src/chord.js: line 32, col 13, Expected '{' and instead saw 'break'.
 src/chord.js: line 141, col 38, Expected '!==' and instead saw '!='.
 src/chord.js: line 174, col 27, Expected '===' and instead saw '=='.
 src/chord.js: line 176, col 34, Expected '===' and instead saw '=='.
 src/chord.js: line 178, col 34, Expected '===' and instead saw '=='.
 src/chord.js: line 178, col 52, Expected '===' and instead saw '=='.
 src/chord.js: line 182, col 27, Expected '===' and instead saw '=='.
 src/chord.js: line 184, col 34, Expected '===' and instead saw '=='.
 src/chord.js: line 186, col 34, Expected '===' and instead saw '=='.
 src/chord.js: line 186, col 52, Expected '===' and instead saw '=='.
 src/chord.js: line 237, col 26, Expected '!==' and instead saw '!='.
 src/chord.js: line 237, col 53, Expected '===' and instead saw '=='.
 src/chord.js: line 238, col 22, Expected '===' and instead saw '=='.
 src/chord.js: line 288, col 25, Expected '===' and instead saw '=='.
 src/chord.js: line 293, col 32, Expected '===' and instead saw '=='.
 src/chord.js: line 40, col 27, 'kQualityLong' is not defined.
 src/chord.js: line 41, col 19, 'kChords' is not defined.
 src/chord.js: line 41, col 27, 'kQualityLong' is not defined.
 src/chord.js: line 42, col 26, 'kQualityLong' is not defined.
 src/chord.js: line 44, col 20, 'kQualityLong' is not defined.
 src/chord.js: line 45, col 19, 'kChords' is not defined.
 src/chord.js: line 45, col 27, 'kQualityLong' is not defined.
 src/chord.js: line 46, col 26, 'kQualityLong' is not defined.
 src/chord.js: line 48, col 19, 'kChords' is not defined.
 src/chord.js: line 288, col 52, 'kStepNumber' is not defined.
 src/chord.js: line 289, col 21, 'kIntervals' is not defined.
 src/chord.js: line 289, col 32, 'kIntervalIndex' is not defined.
 src/chord.js: line 291, col 47, 'kStepNumber' is not defined.
 src/chord.js: line 250, col 63, 'num' is defined but never used.
 src/scale.js: line 8, col 20, Expected '===' and instead saw '=='.
 src/scale.js: line 48, col 16, Expected '===' and instead saw '=='.
 src/scale.js: line 50, col 23, Expected '===' and instead saw '=='.
 src/scale.js: line 52, col 23, Expected '===' and instead saw '=='.
 src/scale.js: line 54, col 23, Expected '===' and instead saw '=='.
 src/scale.js: line 56, col 23, Expected '===' and instead saw '=='.
 src/scale.js: line 58, col 23, Expected '===' and instead saw '=='.
 src/scale.js: line 60, col 23, Expected '===' and instead saw '=='.
 src/scale.js: line 68, col 18, Expected '===' and instead saw '=='.
 src/scale.js: line 70, col 25, Expected '===' and instead saw '=='.
 src/scale.js: line 70, col 45, 'kStepNumber' is not defined.
 src/scale.js: line 71, col 22, 'kStepNumber' is not defined.
 src/scale.js: line 77, col 17, 'interval' is defined but never used.
 [ERROR] Lint failed!

This should be fixed, so the new Jake build system can be used properly.

Create Intervals

Hello

I'm looking for a way to create an interval from text, for example

interval = teoria.interval("A3","G4");

right now I'm using only teoria notes to create intervals.

Thanks

Equal Notes

I need to examine if two notes are the same pitch, Gb = F# etc. I wrote this simple fonction. It there another way to do that ?

teoria.checkEqualNotes = function(a,b){

var interval = teoria.interval(teoria.note(a),teoria.note(b)).toString();

if (interval ==='P1' || interval === 'd-2' || interval === 'd2'){
    return true;
} else {
    return false;
}

}

Inverse Interval

I need to implement an inverse interval

P4 -> P-5
m3 -> M-6

Any idea how to do it with the current implemented method, or some new method need to be written ?

Build doesn't work

Is this project still active?

I've followed the build steps, but "jake build" fails - missing underscore. Tried adding underscore manually to the package.json but then it complains about missing console-browserify.

Modular version of teoria

Hi! Thanks for you library.

I'm build a score manipulation library (https://github.com/danigb/scorejs) that it's using teoria, but I need only some parts of it. For me its important make teoria compatible with npm modules.

From the API perspective, what I imagine:

  1. Use as single module, everything works as expected:
var teoria = require('teoria);
teoria.note('c2').interval('M2') ...
  1. Use single classes:
var Note = require('teoria/note')
Note.fromString('c2').tranpose('M3');

Also I would like to split the functionallity of the classes it in modules, something like this:

var Note = require('note');
typeof(Note.fromString('c2').solfege) => 'undefined'
var solfege = require('teoria/note/solfege');
Note.use(solfege):
typeof(Note.fromString('c2').solfege) => 'function'

Of course, If you require the main module, you can use the same api as before out of the box. What I imagine it's a kind of teoria 2.0 version.
What do you think?

Saludos,
Dani

Adding support for Alternate Tunings

Are you interested in adding support for other types of tunings like 5-tet and Pythagorean? Piers Titus has a tuning exploration demo that compares the different musical tunings out there: http://www.toverlamp.org/static/wickisynth/wickisynth.html (this demo only works in Firefox).

I have some code for a tuning class that I was going to add to music.js but honestly I like this library better. If your game I can take a stab at adding a tuning class to teoria.

Please elaborate on teoria.note.fromKey's numbering reference.

I guess it's the key number from a 88 key piano starting at 1? Or is it a midi note number? It would help a lot if you just added this information in the documentation.

I've designed a 61 key piano that start with C2 as it's leftmost key and I'd really like to use fromKey() to get the note, because this way I dont have to bind key/octave knowledge to my html elements. I could just get the index of the clicked key, calculate the offset between my 61 keys and fromKey()'s reference and ta-dah!

Possible erroneous interpretation of chord string.

I'm using ChordFinder.app to help me along my adventure into the complex world of music theory, If I set up the app to show me all the chords for a C double sharp, it provides me with a list of triads, basic sevenths etc... If you notice in the screenshot it uses the ascii-style notation to display chord in the left.

Screen Shot 2013-02-27 at 5 35 24 PM

Now, I dont know if it's ChordFinder, Teoria or my own understanding of things that is at fault here, but grabbing the string from ChordFinder.app for a "Dominant 13th, Sharp 11th Chord starting at C double sharp. I get the keys displayed in the screenshot. Using teoria, I get whole other set of keys.

teoria.chord('C##13#11').notes.toString() #=> c#4,e#4,g#4,b4,d#5,fx5,ax5

I think could either be that teoria only uses x for double sharps and doesn't understand '##'. Or that ChordFinder.app should have used an x. And yes, I know how hard it must have been to decide on these things because I clearly see there is no authoritative standard on chord notation.

Create a chord with root different from the tone

Hi,

Not sure about the correct english name of what i want to explain, an exemple worth 10000 words.

I dont find the way to build such a chord : A/B : A chord with A as the tonic and B as the bass.

Is there a way to do so in the current version ?

TeoriaInterval#octaves() is terribly broken

Works as expected with ascending intervals, but descending intervals go crazy:

teoria.interval('P-5').octaves() == 0 // Expected
teoria.interval('P-4').octaves() == -2 // wtf

Unexpected exception from scaleDegree

The code

teoria.note("Eb4").enharmonics()[0].scaleDegree(teoria.note("E4").scale("chromatic")) 

throws a TypeError: TypeError: Cannot read property 'quality' of undefined.

I don't have a music theory background, so I don't really now if this kind of operation make sense. But according to the README, I think the correct behavior should be returning 0.

Thanks for this awesome library! I'm using it to build an open-source ukulele chord finder for mobile web.

Evaluating scale degree of enharmonics acquired from TeoriaNote.enharmonics() can fail

Here is a snippet that causes the error

function test() {
    var aNote = teoria.note( 'db3' );
    var aScale = teoria.note( 'f' ).scale( 'major' );

    var enharmonics = aNote.enharmonics();
    for( var i = 0; i < enharmonics.length; i++ ) {
        var note = enharmonics[i];
        if( note.scaleDegree( aScale ) > 0 ) {
            return note;
        }
    }   
}

TeoriaInterval.quality() calls TeoriaInterval.qualityValue(), which is returning 5, which is out of bounds within the kAlterations sub-array that is being accessed. This may be related to #56 as it looks like the qualityValue function is dependent on the range that TeoriaInterval.coord lies within.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.