[Haskell-cafe] Music-related

Johann Bach johann.bach1127 at gmail.com
Sat Aug 7 16:41:02 EDT 2010


Midi files or music exported by the typical music editor is
"expressionless." A *human* would play that music with slight tempo
variations, subtle accents... some notes would be overlapped in time
slightly, some separated in time. I want a program that starts with a
plain midi file, then via a domain-specific language lets me describe
nuances of expression.

(Aside: this is mostly an experiment done for curiosity and an excuse
to write Haskell code. As a practical matter, since I already play
some piano and I am working on improving my music imagination, it
makes more sense to put time into music practice and not
programming. But hey, I don't always make sense.)

Nuances include

- stretching or shrinking small units of time, for example single
  beats within a measure

- inserting pauses before or after a beat, or half-beat, or other
  small unit of time

- changing how much sequential notes overlap (in time) or are
  separated. Be able to do this over a range of time

- similar to previous: over a range of time, make notes overlap in
  time, and vary that overlap from a larger amount to a smaller amount

- accenting (making slightly louder) notes at a specific time

- put in a crescendo (gradually louder) or decrescendo (gradually
  softer) over a span of time

Examples: stretch time between beats 3 and 4, measure 7:

m7 [ 3 --> 4 time:+105% ]

Change overlap of beat 3 to following beat by increasing it 100 ms:

m7 [ 3 overlap:+100]

Over the range of beats 1 to 4, stepping by 1/2 beat, gradually
increase the overlap from 100 ms to 200ms:

m7 [3 --> 4 (step 1/2) overlap:+100 --> +200]

It would be useful to define patterns that are used repetitively.

define patternFoo [
  3 --> 4 time:+105%
  3 overlap: +100
  3 --> 4 (step 1/2) overlap:+100 --> +200
]

Then,

m7 [ patternFoo ]

Often music has a hierarchy of expressive elements: there is some
larger, basic expressive pattern, while individual measures deviate
slightly from that. So it is useful to invoke a pattern, then use the
language to describe additional changes:

m7 [
  patternFoo
  1 --> 2 overlap:+50
]

In this case, patternFoo already defines an "overlap value" for beat
1, so the additional mention of overlap here is *summed* with the
existing value.

I want to solicit suggests for the basic data structure to represent
the expression descriptors.

First, note the job of the software in translating a "flat" midi file
into an expressive one can be expressed like this:

- For every note in the midi file
  - find out what overlap applies to that note
  - find out what dynamic changes apply to that note
  - find out what time the note occurs at
  - etc.
  - finally compute the note's actually begin time, end time, and
    dynamic

So the basic problem to solve is: given a list of expressive element
descriptors, and the begin time of a note (specified as a measure and
beat), figure out which descriptors are "in effect" at the given time,
and sum them.

Let's look just at descriptors that stretch or shrink time. Let's say
that for measure 7, we have the following two descriptors present:

   - 1 --> 2 1/2 time:+110%
   - 2 --> 2 2/3 time:+120%

Note: 2 1/2 is "two and a half", 2 2/3 is "two and two-thirds."

They overlap in time. I wonder what is the best form to store this
information in order to facilitate translating a note? I'm thinking of
this: each descriptor becomes a pair of elements: an "on" element and
an "off" element.

  - beat 1:     time: +110  ON
  - beat 2:     time: +120  ON
  - beat 2 1/2: time: +110 OFF
  - beat 2 2/3: time: +120 OFF

For a note at beat X, you run though this list of on/off elements,
tracking the total changes to elements like 'time', until you reach
beat X.


More information about the Haskell-Cafe mailing list