noteflakes / lydown

A language for music notation
http://ciconia.github.io/lydown
MIT License
24 stars 0 forks source link

Measure number indicators #38

Open noteflakes opened 9 years ago

noteflakes commented 9 years ago

Measure number indicators are used signify the absolute position of anything that comes after the indicator in terms of the referenced measure. Lydown should automatically add rests (for actual parts) or silences (for the global context - see #37).

An example:

- time: 4/4
- key: D
(20): || // this will put a double bar line at the beginning of measure 20
(35): \fine // this puts a _fine_ indication at the beginning of measure 35...
(37:3): \fine // this puts a _fine_ indication on the third beat of measure 37
(end): \dacapoalfine // this puts a _da capo al fine_ indication at the end of the piece

The (end) measure indicator requires lydown to go over the other parts in the same movement and to calculate the maximum number of measures.

noteflakes commented 8 years ago

As preparation for this feature, and also for being able to do vertical editing in interactive mode (see #50), we need to have a context. We can use the opts object as context. Since every file is parsed separately, and usually represents a separate part, we can safely do the time calculation in the parsing stage, at least for 99% of use cases. The other 1% could be dealt with by preserving the current time in the opts object passed to LydownParser.parse.

noteflakes commented 8 years ago

Actually, trying to assign times to each event in a lydown stream is too problematic. There's problem with time signatures changed in mid stream, duration macros, named macros, and a global stream with measure indicators and time signature changes, that would cause an implementation based on the parsing stage to be a total chaos.

Therefore, we are left with the option of doing the time calculation during the translation stage, which creates the actual lilypond code. What we can do is instead of assigning a time for each event, rather create a hash mapping musical time to a line, column tuple.

We add a MusicalTime class:

class MusicalTime
  attr_reader :measure, :time

  def initialize(measure, time)
    @measure, @time = measure, time
  end
end

The way the algorithm works, it assigns the next time value to the first duration/note/rest/silence event that it finds.

A simple example:

4c8eg2c

Should lead to the following map (where time is expressed in Rational values):

(1 0)   => [1,2] # c
(1 1/4) => [1,4] # e
(1 3/8) => [1,5] # g
(1 2/4) => [1,7] # c

An example with a duration macro:

{8_6__}cdedef2e

And the time map:

(1 0)    => [1,8] # c
(1 1/8)  => [1,9] # d
(1 3/16) => [1,10] # e
(1 2/8)  => [1,11] # d
(1 3/8)  => [1,12] # e
(1 7/16) => [1,13] # f
(1 2/4)  => [1,15] # e

An example with multiple lines:

4c8d6e3fg4ab
8.c6d8.e6f2g

The time map:

(1 0)     => [1,2] # 4c
(1 1/4)   => [1,4] # 8d
(1 3/8)   => [1,6] # 6e
(1 7/16)  => [1,8] # 3f
(1 15/32) => [1,9] # 3g
(1 1/2)   => [1,11] # 4a
(1 3/4)   => [1,12] # 4b
(2 0)     => [2,3] # 8.c
...