saebekassebil / teoria

Javascript taught Music Theory
http://saebekassebil.github.io/teoria
MIT License
1.32k stars 114 forks source link

Modular version of teoria #78

Open danigb opened 9 years ago

danigb commented 9 years ago

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

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

saebekassebil commented 9 years ago

I think this is absolutely fantastic! This is exactly where I want this library to go - I've already done some work on the "modularado" branch, but I'm not finished yet.

I've already ripped some of the parts out into their own modules (d'accord, scientific notation, helmholtz, etc.) and I imagine starting to use these as dependencies.

But first I think we should settle for an API compatible update to the new structure - a refactoring of the code, so we can throw out Jake and my lousy "mingler" and start using browserify or something similar.

Looking very much forward to testing out your score manipulation module!

saebekassebil commented 9 years ago

Hey @danigb!

Take a look at master now! I've merged the modulorado branch, and made it backwards-compatible. I don't want to keep it that way, but for now it removed a lot of unwanted clutter.

What do you think about

var Note = require('teoria').Note;
// instead of:
var Note = require('teoria/note');

This will allow for some fancy ES6 in the (near!) future

var { Note, Scale, Chord } = require('teoria');
danigb commented 9 years ago

Hi! Very happy to see the modulorado marged into master, great! Good work (y)

I see a problem with Note = require('teoria').Note. The problem is that require('teoria') will load all the module (with chords and scales), but I want only the Note class.

Maybe extract the Note class into a different repository?

I will take a look to your changes. Thanks

saebekassebil commented 9 years ago

Yes Note, Chord, Scale, etc. all need to go into their separate module. Just not sure what's the right way of doing it. I think creating teoria-note and teoria-chord, etc. modules, makes them too coupled with the teoria code-base. We'd want something a bit more general.

And this is where I'm in doubt. How many of the methods in the Note module belongs to that core Note module? And how many should go in separate module? And what format would these modules exchange?

I've stuck thinking about this for quite a while, so input is very much appreciated - and thank you for looking into the changes!

danigb commented 9 years ago

After working with your code for a couple of days, thats what I imagine:

I would start with three submodules (you're good naming modules ;-):

Some plugins can be inside note module but not loaded by default: var Note = require('note').use(require('note/midi'), require('note/transpose')).

Something like this:

// note-midi.js source:

module.exports = function(Note) {
  Note.fromMidi = function(...) { ... return new Note(...) }
  Note.prototype.toMidi = function() { ... }
}

and:

Note.prototype.use = function() { 
  for(var i = 0; i < arguments.length; i++) { arguments[i](this); }
}

Everything else implemented in teoria, and when that works, I would look at Chord and Scale. I think teoria would end depending on: vector, interval, note, chord and scale. And chord and scale depends on note.

What do you think?

saebekassebil commented 9 years ago

I'm not sure I like the plugin idea. It'd be better to have modules that took a note object (or just the note coordinate!) and returned a result of some calculation it'd done. Then higher-level modules, like teoria, could stitch all the functionality together and make them a bit nicer to use?

// note-to-key module
module.exports = function(coord) {
  return coord[0] * 12 + coord[1] * 7 + 49;
}
// teoria module, note section
var toKey = require('note-to-key');

var Note = { ... };
Note.prototype.toKey = function() {
  return toKey(this.coord);
}
danigb commented 9 years ago

Ajá... I see... you mean something like... ?

var solfege = require('solfege');
solfege(note, scale, true) => ...
saebekassebil commented 9 years ago

Yes!

danigb commented 9 years ago

When I was thinking about teoria refactoring, the idea of a "pitch" module always come to me. Something that integrates interval with the minimun core of note:

var Pitch = require('pitch');
Pitch.parse('c2') => [x, y]
Pitch.toString([x, y]) => 'c2'
Pitch.interval('M2') => [a, b]
Pitch.interval([a, b]) => "M2"
Pitch.transpose([x, y], [a, b]) => [m, n]

Maybe this is the low level module we are looking for that standarizes vector as exchange format? (in fact this is exactly what I need right now for scorejs... ;-)

saebekassebil commented 9 years ago

At the core a note is actually an interval. An interval relative to some other note. If we say that A4 is [0, 0], then [-1, 2] is at the same time a B4 and a Major 2nd (M2) interval.

I don't think this needs any wrapping, like a pitch module. It's just a tuplet - or in this case an array with two elements. One representing octaves to jump, the other representing fifths to jump relative to A4 (or whatever is chosen as root).

Maybe we could just make a flood of simple modules, which all does one thing: Some calculation on the two-element array. Then finally we could bundle up all these in higher-level modules, so people could choose the level they found appropriate?

danigb commented 9 years ago

A cascade of calculations over the two-element array sounds very nice to me!

danigb commented 9 years ago

By the way, where did you find this algorithm/representation?

saebekassebil commented 9 years ago

Stole it from Greg Jopa's MusicJS - An early, now discontinued music theory framework.

More here: http://www.gregjopa.com/2011/05/calculate-note-frequencies-in-javascript-with-music-js/

danigb commented 9 years ago

Great to know it! I've googled it but I did't found it. Thanks

+1 to the calculation modules. great idea (scientific-notation is the first of them ;)

danigb commented 9 years ago

I'm excited with the micro-plugins idea. Are you going to work on this? Do you want some help?

danigb commented 9 years ago

Inspired by your idea and the underscore library, I build a test of how I think you can split teoria (or at least a big part of it). https://github.com/danigb/pitch

I understand this is basically your code, so if you like the approach, I don't have any problem in transfer ownership.

Saludos