teoria
teoria copied to clipboard
Make it possible to create interval from semitones
I was looking for a way to create an interval from number of semitones from a reference note, something similar with music.js's Interval.fromSemitones().
It's a bit verbose (please let me know if there's a better way), but it works as expected:
teoria.Interval().fromSemitones(0).name() // => 'unison'
teoria.Interval().fromSemitones(1).name() // => 'second'
I also added a helpful method to transpose a note without mutating it:
c = teoria.note('c')
c.name() // => 'c'
c.transposeNew(teoria.Interval().fromSemitones(1)).name() // => 'd'
c.name() // => 'c' (no changes on original note)
With the original Note.transpose()
method we change the note itself:
c = teoria.note('c')
c.name() // => 'c'
c.transpose(teoria.Interval().fromSemitones(1)).name() // => 'd'
c.name() // => 'd' (note changed)
Please let me know if there's something I can improve on the PR. I'm actually using it on this live coding language where it's handy to create notes from intervals defined by semitones.
Im currently working on a music theory library as well, and had to tackle the same issue.
A number of semitones is not enough to define an interval, because of the existence of enharmonic intervals (ex augmented fourth and diminished 5th both have a distance of 6 semitones).
If you only care about the pitch class or the frequency of the resulting notes, this is not a problem, and will indeed work, but this can not be implemented naively in a musical theory library imo
AS @AtActionPark is saying, this isn't really possible to do "correctly", because an interval of 6 semitones can both be an #4 or b5, and 2 semitones can be M2 og d3. What I think is doable is to make a function that returns all possible intervals for a given semitone-defined interval. So:
Interval.fromSemitones(6) -> ['A4', 'd5']
ordered after what interval is most common. (M2 over d3, etc).
I think i've already implemented something like that in https://github.com/saebekassebil/piu - maybe you could rip that functionality out, make it its own module, and I'd be happy to include it in teoria :)