This improves things a bit with respect to global attributes being applied too
often. Usually you don't notice because we're just applying the same attribute
values multiple times, but it's particularly noticeable with things like
(octave! 'up).
I haven't quite 100% fixed the (octave! 'up) case yet. I think I also need to
make it so that global attribute changes don't immediately apply the attribute
change, because then it'll just get applied again right before the next
note/rest.
This provides filename/line/column context for any error I could possibly find.
To achieve this, I made it so that all ScoreUpdates have source context, so when
updating the score results in an error, we can print out the line and column
number corresponding to the error.
It's a bit more complicated than that, because score updates are nestable (e.g.
event sequences can contain cram expressions can contain chords, etc.), which
essentially gives us a stacktrace whenever a score update results in an error. I
punted on giving the user the full stacktrace, in favor of doing something
simpler and just showing the bottom-most error, because that's probably going to
be the line and column number that's the most helpful.
Notionally, I can't think of a reason why Beats() should ever return an error. I
think the only reason why I included an error argument was so that I could
return a clear non-value in the case of miliseconds => beats conversion. But
milliseconds => beats conversion _is_ possible, it's just that I've been punting
on it (and am continuing to punt now). Eventually, if I want to implement
Beats() for NoteLengthMs, I'll need to adjust the signature again to include a
tempo argument (i.e. `Beats(tempo float64)`). Until then, I think it's OK to
panic in that scenario. In practice, it shouldn't be possible for the end user
to go down that code path.
Related necessary REPL changes:
* In normal REPL usage, suppress tempo messages because they complicate things
and we don't need them.
* When exporting a score via the REPL, we reload the score into a fresh player
process first, then we _do_ include the tempo messages because we need the
exported MIDI to contain tempo messages.
* Omit "duration" on notes, rests and crams when the duration value is implicit
* Omit "slurred?" on notes when false, for brevity.
* Fix an issue I just noticed where `.Set(...)` doesn't work with the
declarative JSON thing I'm doing. Sigh.
Emitter always sounded weird to me the way I've been using it. I looked up the
difference in the definitions and Transmit definitely makes more sense, not to
mention it sounds better.