Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell98 |
Bel(R) is a simplified form of the Bel notation described in:
- Bernard Bel. "Time and musical structures". Interface (Journal of New Music Research) Volume 19, Issue 2-3, 1990. (http://hal.archives-ouvertes.fr/hal-00134160)
- Bernard Bel. "Two algorithms for the instantiation of structures of musical objects". Centre National de la Recherche Scientifique, 1992. GRTC 458 (http://www.lpl.univ-aix.fr/~belbernard/music/2algorithms.pdf)
For patterns without tempo indications, the two notations should give equivalent phase diagrams, for instance (Bel 1990, §11, p.24):
> bel_ascii_pp "ab{ab,cde}cd" Bel(R): "ab{ab,cde}cd", Dur: 7 a _ b _ a _ _ b _ _ c _ d _ c _ d _ e _
and:
> bel_ascii_pp "{a{bc,def},ghijk}" Bel(R): "{a{bc,def},ghijk}", Dur: 5 a _ _ _ _ _ _ _ _ _ b _ _ _ _ _ _ _ _ _ _ _ _ _ _ c _ _ _ _ _ _ _ _ _ _ _ _ _ _ d _ _ _ _ _ _ _ _ _ e _ _ _ _ _ _ _ _ _ f _ _ _ _ _ _ _ _ _ g _ _ _ _ _ _ _ h _ _ _ _ _ _ _ i _ _ _ _ _ _ _ j _ _ _ _ _ _ _ k _ _ _ _ _ _ _
The Bel notation allows n-ary parallel structures,
ie. {a_bcd_e,a_f_gh_,ji_a_i_}
(Bel 1992, p.29), however Bel(R)
allows only binary structures. The parallel interpretation rules are
associative:
> bel_ascii_pp "{a_bcd_e,{a_f_gh_,ji_a_i_}}" Bel(R): "{a_bcd_e,{a_f_gh_,ji_a_i_}}", Dur: 7 a _ b c d _ e a _ f _ g h _ j i _ a _ i _
Bel(R) does allow unary parallel structures (see Iso
), which can
be used to isolate tempo changes:
> bel_ascii_pp "ab{*2cd}ef{*2/3gh}ij" Bel(R): "ab{*2cd}ef{*2/3gh}ij", Dur: 10 a _ b _ c d e _ f _ g _ _ h _ _ i _ j _
Patterns with tempo indications have completely different meanings in Bel and Bel(R), though in both cases parallel nodes delimit the scope of tempo markings.
Bel(R) replaces the /n
notation for explicit tempo marks with a
*n
notation to indicate a tempo multiplier, and a set of bracketing
notations to specify interpretation rules for parallel (concurrent)
temporal structures.
The tempo indication /1
in the expression ab{/1ab,cde}cd
(Bel 1990, p.24) requires that the inner ab
have the same tempo as
the outer ab
, which is implicitly /1
. Setting the tempo of one
part of a parallel structure requires assigning a tempo to the other
part in order that the two parts have equal duration. Here the tempo
assigned to cde
is /1.5
, but since fractional tempi are not
allowed the expression is re-written as /2ab{/2ab,/3cde}/2cd
.
Importantly the explicit tempo indications make it possible to write
syntactically correct expressions in Bel that do not have a coherent
interpretation, ie. {/1ab,/1cde}
. Determining if a coherent set
of tempos can be assigned, and assigning these tempos, is the object
of the interpretation system.
In comparison, all syntactically valid Bel(R) strings have an
interpretation. The expression {*1ab,*1cde}
is trivially equal to
{ab,cde}
, and tempo marks in parallel parts do not interact:
> bel_ascii_pp "{a*2b,*3c/2d/3e}" Bel(R): "{a*2b,*3c*1/2d*1/3e}", Dur: 3 a _ _ _ _ _ b _ _ c d _ e _ _ _ _ _
Here a
is twice the duration of b
, and e
is three times the
duration of d
, which is twice the duration of c
(in Bel(R) /n
is equivalent to *1/n
). The duration of any Bel(R) expression
can be calculated directly, given an initial Tempo
:
bel_dur 1 (bel_char_parse "a*2b") == 3/2 bel_dur 1 (bel_char_parse "*3c/2d/3e") == 3
Therefore in the composite expression the left part is slowed by a factor of two to align with the right part.
The Bel string ab{/1ab,cde}cd
can be re-written in Bel(R) as
either ab~{ab,cde}cd
or ab(ab,cde)cd
. The absolute tempo
indication is replaced by notations giving alternate modes of
interpretation for the parallel structure.
In the first case the ~
indicates the opposite of the normal rule
for parallel nodes. The normal rule is the same as for Bel and is
that the duration of the whole is equal to duration of the longer of
the two parts. The ~
inverts this so that the whole has the
duration of the shorter of the two parts, and the longer part is
scaled to have equal duration.
In the second case the parentheses ()
replacing the braces {}
indicates that the duration of the whole is equal to the duration of
the left side, and that the right is to be scaled. Similarly, a ~
preceding parentheses indicates the duration of the whole should be
the duration of the right side, and the left scaled.
> bel_ascii_pp "ab~{ab,cde}cd" Bel(R): "ab~{ab,cde}cd", Dur: 6 a _ _ b _ _ a _ _ b _ _ c _ _ d _ _ c _ d _ e _
There is one other parallel mode that has no equivalent in Bel notation. It is a mode that does not scale either part, leaving a hole at the end of the shorter part, and is indicated by square brackets:
> bel_ascii_pp "ab[ab,cde]cd" Bel(R): "ab[ab,cde]cd", Dur: 7 a b a b c d c d e
The Bel string /2abc/3de
(Bel 1992, p.53) can be written as
*2abc*1/2*3de
, or equivalently as *2abc*3/2de
:
> bel_ascii_pp "*2abc*3/2de" Bel(R): "*2abc*3/2de", Dur: 13/6 a _ _ b _ _ c _ _ d _ e _
It can also be written using the shorthand notation for rest sequences, where an integer n indicates a sequence of n rests, as:
> bel_ascii_pp "(9,abc)(4,de)" Bel(R): "(---------,abc)(----,de)", Dur: 13 - - - - - - - - - - - - - a _ _ b _ _ c _ _ d _ e _
In the Bel string {ab{/3abc,de},fghijk}
(Bel 1992, p.20) the tempo
indication does not change the inter-relation of the parts but rather
scales the parallel node altogether, and can be re-written in Bel(R)
notation as:
> bel_ascii_pp "{ab*3{abc,de},fghijk}" Bel(R): "{ab*3{abc,de},fghijk}", Dur: 6 a _ _ _ _ _ b _ _ _ _ _ a _ b _ c _ d _ _ e _ _ f _ _ g _ _ h _ _ i _ _ j _ _ k _ _
Curiously the following example (Bel 1990, p. 24) does not correspond to the phase diagram given:
> bel_ascii_pp "{i{ab,cde},jk}" Bel(R): "{i{ab,cde},jk}", Dur: 4 i _ a _ _ b _ _ c _ d _ e _ j _ _ _ k _ _ _
The paper assigns tempi of /6
to both i
and ab
, which in
Bel(R) could be written:
> bel_ascii_pp "{i~{ab,cde},jk}" Bel(R): "{i~{ab,cde},jk}", Dur: 3 i _ _ _ _ _ a _ _ _ _ _ b _ _ _ _ _ c _ _ _ d _ _ _ e _ _ _ j _ _ _ _ _ _ _ _ k _ _ _ _ _ _ _ _
- data Par_Mode
- par_mode_brackets :: Par_Mode -> (String, String)
- bel_brackets_match :: (Char, Char) -> Bool
- type Tempo = Rational
- data Term a
- data Bel a
- bel_pp :: (a -> String) -> Bel a -> String
- bel_char_pp :: Bel Char -> String
- par_analyse :: Tempo -> Par_Mode -> Bel a -> Bel a -> (Rational, Rational, Rational)
- par_dur :: Tempo -> Par_Mode -> Bel a -> Bel a -> Rational
- bel_tdur :: Tempo -> Bel a -> (Tempo, Rational)
- bel_dur :: Tempo -> Bel a -> Rational
- type Time = Rational
- type Voice = [Char]
- type L_St = (Time, Tempo, Voice)
- type L_Term a = (L_St, Term a)
- lterm_time :: L_Term a -> Time
- lterm_duration :: L_Term a -> Time
- lterm_end_time :: L_Term a -> Time
- type L_Bel a = [L_Term a]
- bel_linearise :: L_St -> Bel a -> (L_Bel a, L_St)
- lbel_merge :: L_Bel a -> L_Bel a -> L_Bel a
- lbel_tempi :: L_Bel a -> [Tempo]
- lbel_tempo_mul :: Rational -> L_Bel a -> L_Bel a
- lbel_normalise :: L_Bel a -> L_Bel a
- voice_normalise :: Voice -> Voice
- voice_eq :: Voice -> Voice -> Bool
- lbel_voices :: L_Bel a -> [Voice]
- lbel_duration :: L_Bel a -> Time
- lbel_lookup :: (Time, Voice) -> L_Bel a -> Maybe (L_Term a)
- lbel_grid :: L_Bel a -> [[Maybe (Term a)]]
- bel_grid :: Bel a -> [[Maybe (Term a)]]
- bel_ascii :: Bool -> Bel Char -> String
- bel_ascii_pr :: Bel Char -> IO ()
- (~>) :: Bel a -> Bel a -> Bel a
- lseq :: [Bel a] -> Bel a
- node :: a -> Bel a
- nseq :: [a] -> Bel a
- cseq :: String -> Bel Char
- par :: Bel a -> Bel a -> Bel a
- rest :: Bel a
- nrests :: Integral n => n -> Bel a
- bel_parse_pp_ident :: String -> Bool
- bel_ascii_pp :: String -> IO ()
- type P a = GenParser Char () a
- p_rest :: P (Term a)
- p_nrests :: P (Bel a)
- p_continue :: P (Term a)
- p_char_value :: P (Term Char)
- p_char_term :: P (Term Char)
- p_char_node :: P (Bel Char)
- p_integer :: P Integer
- p_rational :: P Rational
- p_double :: P Double
- p_number :: P Rational
- p_mul :: P (Bel a)
- p_iso :: P (Bel a) -> P (Bel a)
- p_char_iso :: P (Bel Char)
- p_par :: P (Bel a) -> P (Bel a)
- p_char_par :: P (Bel Char)
- p_char_bel :: P (Bel Char)
- bel_char_parse :: String -> Bel Char
Bel
Types of Par
nodes.
par_mode_brackets :: Par_Mode -> (String, String) Source
The different Par
modes are indicated by bracket types.
bel_brackets_match :: (Char, Char) -> Bool Source
Terms are the leaf nodes of the temporal structure.
Recursive temporal structure.
bel_pp :: (a -> String) -> Bel a -> String Source
Pretty printer for Bel
, given pretty printer for the term type.
par_analyse :: Tempo -> Par_Mode -> Bel a -> Bel a -> (Rational, Rational, Rational) Source
Analyse a Par node giving (duration,LHS-tempo-*,RHS-tempo-*).
par_analyse 1 Par_Left (nseq "cd") (nseq "efg") == (2,1,3/2) par_analyse 1 Par_Right (nseq "cd") (nseq "efg") == (3,2/3,1) par_analyse 1 Par_Min (nseq "cd") (nseq "efg") == (2,1,3/2) par_analyse 1 Par_Max (nseq "cd") (nseq "efg") == (3,2/3,1) par_analyse 1 Par_None (nseq "cd") (nseq "efg") == (3,1,1)
Linearisation
Voices are named as a sequence of left and right directions
within nested Par
structures.
lterm_time :: L_Term a -> Time Source
Start time of L_Term
.
lterm_duration :: L_Term a -> Time Source
Duration of L_Term
(reciprocal of tempo).
lterm_end_time :: L_Term a -> Time Source
End time of L_Term
.
lbel_normalise :: L_Bel a -> L_Bel a Source
After normalisation all start times and durations are integral.
voice_normalise :: Voice -> Voice Source
All leftmost voices are re-written to the last non-left turning point.
map voice_normalise ["","l","ll","lll"] == replicate 4 "" voice_normalise "lllrlrl" == "rlrl"
lbel_duration :: L_Bel a -> Time Source
The duration of L_Bel
.
Combinators
bel_parse_pp_ident :: String -> Bool Source
Verify that bel_char_pp
of bel_char_parse
is id
.
bel_ascii_pp :: String -> IO () Source
Run bel_char_parse
, and print both bel_char_pp
and bel_ascii
.
bel_ascii_pp "{i{ab,{c[d,oh]e,sr{p,qr}}},{jk,ghjkj}}"
Parsing
p_rational :: P Rational Source
Parse positive Rational
.
P.parse (p_rational `P.sepBy` (P.char ',')) "" "3%5,2/3"
Parse positive Double
.
P.parse p_double "" "3.5" P.parse (p_double `P.sepBy` (P.char ',')) "" "3.5,7.2,1.0"
Parse positive number as Rational
.
P.parse (p_number `P.sepBy` (P.char ',')) "" "7%2,3.5,3"
p_char_iso :: P (Bel Char) Source
p_iso
of p_char_bel
.
P.parse p_char_iso "" "{abcde}"
p_char_par :: P (Bel Char) Source
p_par
of p_char_bel
.
P.parse p_char_par "" "{ab,{c,de}}" P.parse p_char_par "" "{ab,~(c,de)}"