ut-proj / undertheory

Music-theoretic LFE modules: notes, intervals, keys, scales, modes, melody-generation, etc.
BSD 2-Clause "Simplified" License
2 stars 0 forks source link

Update note abstractions #20

Open oubiwann opened 2 months ago

oubiwann commented 2 months ago

Tasks:

oubiwann commented 2 months ago

Maybe something like this would make sense:

#(note (; arbitrary k/v pairs for data/metadata
        #( ... )
        #( ... )
        ...))              

We could use this in the undertheory project along these lines:

Where value would be note-head value (e.g., half, 8th, quarter, etc.). One or more utility functions could be used to do things like:

And in the undermidi project, like so:

However ... if the k/v pairs will contain different pairs for different libs/use cases, we want to be able to easily match (check for presence/absence) and ordering doesn't matter for this part ... so we probably want to use maps instead of proplists:

#(note #m(...)); map with arbitrary data/metadata

Which gives us this for undertheory use:

And this for the undermidi project:

For notes, ordering does matter, so a proplist is good for that:

#(notes (#(note #m(...))
         #(note #m(...))
        ...))

And for tuplets, use the note definitions:

The fields value and dots would have the same meaning, use, logic, etc., across all projects, so logic for that would live in undertheory. The name field would too, but it wouldn't really be useful outside the project. The undermidi project could take full responsibility for defining fields useful by itself and any other projects consuming its MIDI capabilities, so could handle logic associated withpitch and velocity fields.

Tuplets are a bit of a tricky one and there's a need to be highly-specified with them, since you can do some pretty crazy stuff with tuplets in notation and performance, but you have to be extremely precise with them to get it right. However, for many of the common cases, we can support under-specifying which then get expanded to various defaults, e.g.:

One of the nice things about this tuplet notation (and the fact that we're using Lisp) is that it easily supports nested tuplets. We can represent this:

image

with the following:

'#(quintuplet
   #(notes 
     (#(note #m(pitch 60 velocity 64 value 1/8))
      #(tuplet 3 16 #(notes
                      (#(note #m(pitch 60 velocity 64 value 1/16))
                       #(note #m(pitch 60 velocity 64 value 1/16))
                       #(note #m(pitch 60 velocity 64 value 1/16)))))
      #(note #m(pitch 60 velocity 64 value 1/4))
      #(tuplet 7 32 #(notes
                      (#(note #m(pitch 60 velocity 64 value 1/32))
                       #(note #m(pitch 60 velocity 64 value 1/32))
                       #(note #m(pitch 60 velocity 64 value 1/32))
                       #(note #m(pitch 60 velocity 64 value 1/32))
                       #(note #m(pitch 60 velocity 64 value 1/32))
                       #(note #m(pitch 60 velocity 64 value 1/32))
                       #(note #m(pitch 60 velocity 64 value 1/32)))))))