Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for Alternate Tunings #4

Open
gregjopa opened this issue Aug 6, 2012 · 14 comments
Open

Adding support for Alternate Tunings #4

gregjopa opened this issue Aug 6, 2012 · 14 comments

Comments

@gregjopa
Copy link

gregjopa commented Aug 6, 2012

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.

@saebekassebil
Copy link
Owner

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.

@gregjopa
Copy link
Author

gregjopa commented Aug 6, 2012

Awesome, I will work on adding this tuning class.

@saebekassebil
Copy link
Owner

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?

@gregjopa
Copy link
Author

gregjopa commented Aug 6, 2012

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.

@saebekassebil
Copy link
Owner

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.

@gregjopa
Copy link
Author

gregjopa commented Aug 8, 2012

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.

@saebekassebil
Copy link
Owner

@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:

  • 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?
  • 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?
  • What's the difference between Linear and Irregular tunings? Is it that the linear has a fixed semitone width, and the irregular has not?

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:

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').

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.

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 ;)

@gregjopa
Copy link
Author

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@git.luolix.topwrote:

@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//issues/4#issuecomment-7580847.

@saebekassebil
Copy link
Owner

Hey @gregjopa, any advancements on this? Not that we're in a hurry but I'm curious :)

@gregjopa
Copy link
Author

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.

@saebekassebil
Copy link
Owner

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.

  • The constants in src/core.js
    • kNotes (.distance parameter)
    • kIntervals (.size paramater)
    • Possibly the interval qualities
    • kIntervalSolfege - needs to be fitted to each tuning (if it even makes sense)
    • The internal getDistance() helper function
  • The note parser (TeoriaNote())
    • The #key() function
    • #fq()
    • #enharmonics()
    • #solfege()
  • The fromKey() and fromFrequency() functions
  • The intervals
    • The "short names" (such as 'P5' and 'm3')
    • Both the #from and #between functions

including 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.

@quickredfox
Copy link

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?

@saebekassebil
Copy link
Owner

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.

@quickredfox
Copy link

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants