Open gregjopa opened 12 years ago
Alternate tuning would be a great addition to the library! I've played around with it, and thought about how to implement it optimally, but I haven't really reached any conclusion, so your code would be greatly appreciated, just submit a PR and we'll take a look at it from there.
Awesome, I will work on adding this tuning class.
Great, but is there a need for a complete tuning "class"? When you say tuning do you mean the equal temperaments and the like? Or do you mean everything from western tunings to non-TET, say Indian tunings?
My concern is about the different notes, and their names. For example, are we to implement support for 24 tone scales? Or does we preserve only 12 tones per octave?
I was thinking that a note would be composed of a tuning. This is how we were originally developing it in music.js. Here is an unfinished demo I made last year to showcase the different tuning calculations: http://code.gregjopa.com/html5/audio/music.js/tuning/ (the audio only works in firefox)
You make a good point that handling note names will be difficult and may add to much bloat to the library. I need to give this some more thought on how it would work with teoria.
Cool, I also predict some conflicts with the Interval class, as it presumes 12TET (like the rest of the library). How are developers to make a note jump a "Lesser septimal tritone" up/down. For example the 19TET tuning introduces all these new intervals.
Maybe one could go in a "driver" direction? Where each tuning is implemented as a "driver"/interface, which implement the same methods (such as interval(note, intervalName)
, frequency(note)
, etc) which are called from the Note/Interval class, thus leaving them decoupled from the tuning logic.
I like your idea of implementing each tuning as a "driver". If you look at the music.js code from my tuning demo I think we use a similar approach except we grouped tunings into two classes: Linear and Irregular. These two classes work for frequency calculations but wouldn't work for intervals so each tuning would need its own class/interface. We could start out with 12 tet and then slowly implement the rest of the different tunings out there around the world. Do you know of any other js libraries that use this "driver" design pattern? I'd like to learn more about it.
I started brainstorming this design and created the following gist: https://gist.github.com/3292167. I added a few questions for you in the comments.
@GregJ I'm sure there must exist some, but I think it's just something that I've picked up as a sort of "implementation of an interface" OOP-style. I use it whenever I get the chance, for example the parsers in Subito where they must implement a (simple) interface to be a "valid" parser.
The design pattern is an important question, but I'm afraid that I simply do not have enough knowledge on the subject pt. to make a reasonable decision. I need the following answered:
I'm sorry for this lack of knowledge, and if you can, then please enlighten me on this subject or point me in direction of some articles/books I can read to grasp the subject a little better :)
About the design pattern: I agree totally that it seems only reasonable to start with implementing the 12TET tuning, and then work our way from there. I'd love to see a interface-like implementation of each tuning, which exposes the same methods so that all other logic is blissfully unaware of which tuning is being used. Because of this, I'd also love to decouple the teoria.note
constructor and the setting of tuning. Is it really necessary to be able to produce set of notes in different tunings? Or could it be done by something like:
// Set tuning to 12TET
teoria.tuning('12TET');
var cis5 = teoria.note('C#5');
cis5.fq(); // 554.3652619537442
teoria.note('C4').scale('minor').forEach(function(note) {
console.log(note.fq());
});
// Set tuning to 19TET
teoria.tuning('19TET');
cis5.fq(); // Something else than 554.3652619537442
// ... etc
To answer the questions from the gist:
I've though about making the constructors (all of them) only taking one argument: A parameter object. Seems like a good idea, when we get all these optionals (duration = 4
, dots = 0
, tuning = '12tet'
).
Yes it would need to be "moved" to the tuning class. That does not mean that we remove the method, but that it will work as a proxy for calling the active tuning class.
And of course the #fq
method too.
What about storing all the tuning classes in a hash table? Like
TeoriaTuning.Tunings = {
'12tet': TeoriaTuning12TET,
'19tet': TeoriaTuning19TET,
'22tet': TeoriaTuning22TET,
// ...etc
};
Then we could just check if the tuning was valid by a simple tuning in TeoriaTuning.Tunings
expression.
Right now I can't think of anything they'd need to know from the outside of their own scope - But that might be because I don't fully understand them yet - so let's take that up later.
Phew, what a screed - Hope you have the patience to read it all ;)
Hey Jakob, see my responses inline. FYI, I did not study music theory in school so I am not an expert on tuning systems and I may be wrong about some things. But I think its really interesting that there are so many different tuning systems out there that have their own approach on how to split an octave into a scale of notes w/ defined frequencies.
On Wed, Aug 8, 2012 at 4:17 AM, Jakob Miland notifications@github.comwrote:
@GregJ https://github.com/GregJ I'm sure there must exist some, but I think it's just something that I've picked up as a sort of "implementation of an interface" OOP-style. I use it whenever I get the chance, for example the parsers in Subito https://github.com/saebekassebil/subito where they must implement a (simple) interface to be a "valid" parser.
Ok, thanks for the explanation. I remember interfaces from Java. I checked out the parsers in Subito and it looks like each parser is a singleton and they all implement the same methods so they can all be used in a common way. This is a nice clean design and it makes sense that there is no need to create instances of a parser using "new". The same parser object can be reused just like a tuning object as you pointed out below.
The design pattern is an important question, but I'm afraid that I simply do not have enough knowledge on the subject pt. to make a reasonable decision. I need the following answered:
- In a tuning with more tones per octave (like 19TET or 22TET), _what_are the notes called? All those notes which we doesn't have in our normal western 12TET tuning most have a name?
The note names depend on the tuning system used. For example, with 19tet an A# and Bb have different frequencies and these intervals are used to identify the 7 extra notes. Wikipedia has a nice diagram of this: http://en.wikipedia.org/wiki/19_equal_temperament#Scale_diagram
For all tunings greater than 19TET I think the extra notes have sharps and flats that are doubled and tripled. Check out the note names used here for 31 TET: http://tonalsoft.com/enc/number/31edo.aspx
Tunings like pythagorean Intonation, mean-tone system, just intonation, and well temperament all have 12 notes per octave and are represented with sheet music. They differ on the ratios used for intervals. Some systems have the goal of having a perfect fourth and/or perfect fifth and other systems make compromises to be able to sound the same in different keys.
- How are such notes constructed? Per name or per frequency? If there's no name to, say the 13th, note in a 19TET tuning, then how are they to interact with normal, named, notes. And how will one calculate intervals from it?
I think we can implement letter names for notes in almost all tunings as I mentioned above. An interval is going to be what truly defines a note. This is why music.js used a pair of coordinates on the circle of 5ths to define a note (our design was problematic though with intervals since we would have had to define a circle of fifths for each tuning that had more/less than 12 notes per octave). I think if you know the key, and the number of notes per octave you can represent a note with a letter note name and frequency. I need to code this and test it out... It would be interesting to see if there are any open source software sheet music engravers or synthesizers that support different tuning systems and how they implemented their music theory library. Some tunings like pythagorean intonation depend on the key for calculating frequencies. This is why well temperament and equal temperament were invented, so an instrument wouldn't have to be re-tuned every time you wanted to play in a different key.
- What's the difference between Linear and Irregular tunings? Is it that the linear has a fixed semitone width, and the irregular has not?
Yep, each equal temperament tuning has intervals that are the same fixed size. I think the term semitone only applies to 12-TET. The notes in each irregular tuning are not equal distant to their neighbor notes.
I'm sorry for this lack of knowledge, and if you can, then please enlighten me on this subject or point me in direction of some articles/books I can read to grasp the subject a little better :)
Sure, here are some great resources about tuning systems:
http://cnx.org/content/m11639/latest/ http://www.midicode.com/tunings/ http://en.wikipedia.org/wiki/Musical_temperament
About the design pattern: I agree totally that it seems only reasonable to start with implementing the 12TET tuning, and then work our way from there. I'd love to see a interface-like implementation of each tuning, which exposes the same methods so that all other logic is blissfully unaware of which tuning is being used. Because of this, I'd also love to decouple the teoria.note constructor and the setting of tuning. Is it really necessary to be able to produce set of notes in different tunings? Or could it be done by something like:
// Set tuning to 12TETteoria.tuning('12TET'); var cis5 = teoria.note('C#5');cis5.fq(); // 554.3652619537442 teoria.note('C4').scale('minor').forEach(function(note) { console.log(note.fq());}); // Set tuning to 19TETteoria.tuning('19TET');cis5.fq(); // Something else than 554.3652619537442 // ... etc
Yes, this is a much cleaner design. You would play/display a score it the same tuning so I like having the tuning live in the teoria object instead of being coupled with a note. Then to switch to playing the score in a new tuning you would just have to set the tuning once using teoria.tuning(tuning name);
To answer the questions from the gist: TeoriaNote constructor
I've though about making the constructors (all of them) only taking one argument: A parameter object. Seems like a good idea, when we get all these optionals (duration = 4, dots = 0, tuning = '12tet').
I agree, it just makes the api syntax a little uglier but I don't think there are any other options if you have multiple optional parameters in js.
TeoriaNote#key
Yes it would need to be "moved" to the tuning class. That does not mean that we remove the method, but that it will work as a proxy for calling the active tuning class.
And of course the #fq method too. namedTuning method
What about storing all the tuning classes in a hash table? Like
TeoriaTuning.Tunings = { '12tet': TeoriaTuning12TET, '19tet': TeoriaTuning19TET, '22tet': TeoriaTuning22TET, // ...etc};
Then we could just check if the tuning was valid by a simple tuning in TeoriaTuning.Tunings expression.
Yes, this is much better than using a case statement.
Tuning constructors
Right now I can't think of anything they'd need to know from the outside of their own scope - But that might be because I don't fully understand them yet - so let's take that up later.
Phew, what a screed - Hope you have the patience to read it all ;)
Great responses. Tuning systems are really complex but I think we are making some great progress here. I will update my gist with your feedback over the weekend.
— Reply to this email directly or view it on GitHubhttps://github.com/saebekassebil/teoria/issues/4#issuecomment-7580847.
Hey @gregjopa, any advancements on this? Not that we're in a hurry but I'm curious :)
Hey @saebekassebil, I have been slackin on this. Last time I worked on it I ended up getting really confused and wasn't making any progress. So I did some googling and found an old book about musical temperament:
"An Elementary Treatise on Musical Intervals and Temperament" by Robert Halford M. Bosanquet. Here is the link to the free e-book: https://play.google.com/store/books/details?id=CiwDAAAAQAAJ&rdid=book-CiwDAAAAQAAJ&rdot=1
The book has lots of info on intervals and has a lot of formulas. My plan is to document all the formulas for the different temperaments out there. Then see how we can integrate it into teoria.
I'm slowly beginning to comprehend the extensiveness of the refactoring work this is going to take. Below I've included a list of every function/functionality that needs to be refactored to make it tuning-agnostic. I'd really like to go through with this, but it might take some time.
src/core.js
kNotes
(.distance
parameter)kIntervals
(.size
paramater)kIntervalSolfege
- needs to be fitted to each tuning (if it even makes sense)getDistance()
helper functionTeoriaNote()
)
#key()
function#fq()
#enharmonics()
#solfege()
fromKey()
and fromFrequency()
functions#from
and #between
functionsincluding everywhere these functions are used internally and possibly more... Because of this, I'd really like to implement an elegant solution the first time, and I hope this refactoring might clean up the code a bit too. I'm pt. playing a bit around with different code-patterns and I think I still like the interface approach the most, such that every function that needs something tuning-related just call:
teoria.activeTuning.whatever()
where activeTuning is a tuning interface that implements all the needed functionality for a tuning (a note-name parser, interval measuring, etc.)
I'm posting this here, just for everyone (myself included) to see, so that we might choose a good refactoring strategy.
This is a good thread, I wish I was as advanced as you guy on the topic! But for what it's worth, when I read @gregjopa's line "This is why well temperament and equal temperament were invented, so an instrument wouldn't have to be re-tuned every time you wanted to play in a different key." I ask myself then: Is it not the instrument that need to be tuned and not the whole scale, notes and chords?
My understanding of music theory is barely a month old but what I understand from Boesendorfer Pianos is that a musician will read the same sheet music, will play the same keys, but the tuning is entirely different. I'm not clear now on exactly how this applies to your implementation ideas, but I thought maybe a little abstract thinking could help. It could for example, be the job of an Instrument class to adapt itself to a Scale according it's Tuning?
Actually tuning and temperament isn't the same. Tuning is how you tune your instrument, that is screwing the nuts on the guitar head, or inside the piano to tighten or loosen the strings to fit into the temperament.
The temperament describes how the notes "relates" to each other - how big interval (in cents/hertz/ratio) is there between them.
For example the temperament we use in modern western music is called 12TET - Twelve tone equal temperament - we've cut up the octave in twelve exactly similar pieces. This is however a (gross) approximation for a "just" tuning - which relates the tones by ratios and sounds much richer, but only in one key. For example if the tune you're playing modulates from F major to Bb major, then suddenly Bb will sound odd in some weird places, because some of the intervals (relative to Bb - the new root), aren't the correct size.
I'm really puzzled on how to implement this correctly, and in a way that makes it possible to extend with a lot of other temperaments, but I hope that we can find some good way of creating a general interface for a temperament.
I see. Thank you for the explaining the distinction. Until I get my nose deep into the code I can't be much help on this, but at least I'll have it in my back-burner as I play around in the source!
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.