diff --git a/Aoide.cabal b/Aoide.cabal
--- a/Aoide.cabal
+++ b/Aoide.cabal
@@ -1,30 +1,66 @@
-author: Liisi Kerik
-build-type: Simple
-cabal-version: >= 1.10
-category: Composition, Music
-description:
-  This library provides data structures for describing music and generates Lilypond and MIDI files. In addition, it contains a
-  module with some music-theoretical functions that may be useful in computer-assisted composition. The library is focused on
-  the very basics of standard Western musical notation and does not support dynamic indications, articulation marks, double
-  accidentals, tempo changes, polyrhythms, time signature changes and microtonality. Tuplets are supported in MIDI scores but
-  not in Lilypond scores. Some features, like key changes and polytonality, can be used but not properly notated.
-homepage: https://github.com/liisikerik/aoide
-license: BSD3
-license-file: LICENSE
-maintainer: liisikerik@hotmail.com
-name: Aoide
-synopsis: A simple music library with the capability of generating .ly and .mid files.
-version: 0.1.0.2
-library
-  build-depends:
-    base >= 4.12.0 && < 4.15,
-    bytestring >= 0.10.8 && < 0.11,
-    mtl >= 2.2.2 && < 2.3,
-    process >= 1.6.3 && < 1.7,
-    template-haskell >= 2.14.0 && < 2.17
-  default-language: Haskell2010
-  exposed-modules: Composition.Lilypond, Composition.Midi, Composition.Notes, Composition.Theory
-  other-extensions: DeriveLift, NegativeLiterals, StandaloneDeriving, TemplateHaskell
-source-repository head
-  location: https://github.com/liisikerik/aoide.git
+cabal-version: 3.8
+author: Liisi Kerik
+category: Composition, Lilypond, MIDI, Music
+description:
+  This library provides data structures and a custom file format for describing music and generates Lilypond and MIDI files. In
+  addition it contains a module with some music-theoretical functions. The library is focused on the very basics of standard
+  Western musical notation and does not support dynamic indications, articulation marks, double accidentals, tempo changes,
+  polyrhythms, time signature changes and microtonality.
+license: BSD-3-Clause
+license-file: LICENSE
+maintainer: liisikerik@hotmail.com
+name: Aoide
+synopsis: A simple music library that can generate Lilypond and MIDI files.
+version: 1.0.0.0
+common Common
+  build-depends:
+    Kawaii-Parser, barbies, base < 4.22, bytestring, containers, directory, filepath, generic-lens, lens, mtl, process, transformers
+  default-extensions:
+    DeriveGeneric,
+    DuplicateRecordFields,
+    FlexibleContexts,
+    FlexibleInstances,
+    GADTs,
+    NamedFieldPuns,
+    NegativeLiterals,
+    NoFieldSelectors,
+    OrPatterns,
+    OverloadedLabels,
+    QuantifiedConstraints,
+    RankNTypes,
+    ScopedTypeVariables,
+    StandaloneDeriving,
+    TypeData,
+    UndecidableInstances
+  default-language: Haskell2010
+  ghc-options: -Wall -Wno-x-partial
+library
+  import: Common
+  exposed-modules:
+    Composition.Errors,
+    Composition.Keyboard,
+    Composition.Lilypond,
+    Composition.MIDI,
+    Composition.Notes,
+    Composition.Parser,
+    Composition.Score,
+    Composition.Theory,
+    Composition.Time,
+    Composition.Write
+executable Aoide
+  import: Common
+  main-is: Main.hs
+  other-modules:
+    Composition.Errors,
+    Composition.Keyboard,
+    Composition.Lilypond,
+    Composition.MIDI,
+    Composition.Notes,
+    Composition.Parser,
+    Composition.Score,
+    Composition.Theory,
+    Composition.Time,
+    Composition.Write
+source-repository head
+  location: https://github.com/liisikerik/aoide.git
   type: git
diff --git a/Composition/Errors.hs b/Composition/Errors.hs
new file mode 100644
--- /dev/null
+++ b/Composition/Errors.hs
@@ -0,0 +1,95 @@
+{-|
+Description: Errors.
+-}
+module Composition.Errors (Error (..), Is_out_of_range_type (..), write_error) where
+  import Parser.Files
+  import Parser.Locations
+  import Parser.Utilities
+  -- | Errors.
+  data Error =
+    An_event_crosses_the_bar_line | ---
+    Conflicting_key_signature | ---
+    Duplicate_header_fields Location | ---
+    Duplicate_keys Location | ---
+    Duplicate_note_names_in_key_signature Location | ---
+    Duplicate_notes Location | ---
+    Empty_triplet | ---
+    File_error File_error | ---
+    Invalid_character_in_text | ---
+    Invalid_keyword String | ---
+    Invalid_note_length Location | ---
+    Invalid_note_name Location | ---
+    Invalid_time_numerator_factor Location | ---
+    Invalid_word String Location | ---
+    Is_out_of_range_with_location Is_out_of_range_type Location |
+    Is_out_of_range_without_location Is_out_of_range_type |
+    Non_positive_note_length | ---
+    Note_length_denominator_contains_factors_other_than_2_and_3 | ---
+    Parse_error Location | ---
+    Track_ends_with_an_incomplete_triplet | ---
+    Track_length_mismatch | ---
+    Velocity_out_of_range Location ---
+  -- | Out of range error types.
+  data Is_out_of_range_type =
+    Event_or_part_length_IOOR |
+    Initial_position_IOOR |
+    Key_IOOR |
+    MIDI_instrument_code_IOOR |
+    Note_IOOR |
+    Note_length_denominator_IOOR | ---
+    Tempo_IOOR |
+    The_least_common_denominator_of_note_lengths_IOOR |
+    The_number_of_pitched_tracks_IOOR |
+    The_number_of_tracks_IOOR |
+    Track_length_IOOR |
+    Velocity_IOOR
+  deriving instance Show Error
+  deriving instance Show Is_out_of_range_type
+  -- | Writing errors. ---
+  write_error :: Error -> String ---
+  write_error err = ---
+    case err of ---
+      An_event_crosses_the_bar_line -> "An event crosses the bar line." ---
+      Conflicting_key_signature -> "Conflicting key signature." ---
+      Duplicate_header_fields location -> "Duplicate header fields at " <> write_location location <> "." ---
+      Duplicate_keys location -> "Duplicate keys at " <> write_location location <> "." ---
+      Duplicate_note_names_in_key_signature location -> ---
+        "Duplicate note names in key signature at " <> write_location location <> "." ---
+      Duplicate_notes location -> "Duplicate notes at " <> write_location location <> "." ---
+      Empty_triplet -> "Empty triplet." ---
+      File_error err' -> ---
+        case err' of ---
+          Failed_to_find_the_file file_path -> "Failed to find the file " <> file_path <> "." ---
+          Invalid_file_path -> "Invalid file path." ---
+          Unexpected_extension ext -> "Unexpected extension ." <> ext <> "." ---
+      Invalid_character_in_text -> "Invalid character in text." ---
+      Invalid_keyword keyword -> "Invalid keyword " <> keyword <> "." ---
+      Invalid_note_length location -> "Invalid note length at " <> write_location location <> "." ---
+      Invalid_note_name location -> "Invalid note name at " <> write_location location <> "."
+      Invalid_time_numerator_factor location -> "Invalid time numerator at " <> write_location location <> "." ---
+      Invalid_word word location -> "Invalid word " <> word <> " at " <> write_location location <> "." ---
+      Is_out_of_range_with_location typ location ->
+        write_is_out_of_range_type typ <-> "at" <-> write_location location <-> "is out of range."
+      Is_out_of_range_without_location typ -> write_is_out_of_range_type typ <-> "is out of range."
+      Non_positive_note_length -> "Non-positive note length." ---
+      Note_length_denominator_contains_factors_other_than_2_and_3 -> ---
+        "Note length denominator contains factors other than 2 and 3." ---
+      Parse_error location -> "Parse error at " <> write_location location <> "." ---
+      Track_ends_with_an_incomplete_triplet -> "Track ends with an incomplete triplet." ---
+      Track_length_mismatch -> "Track length mismatch." ---
+      Velocity_out_of_range location -> "Velocity out of range at " <> write_location location <> "." ---
+  write_is_out_of_range_type :: Is_out_of_range_type -> String
+  write_is_out_of_range_type typ =
+    case typ of
+      Event_or_part_length_IOOR -> "Event or part length"
+      Initial_position_IOOR -> "Initial position"
+      Key_IOOR -> "Key"
+      MIDI_instrument_code_IOOR -> "MIDI instrument code"
+      Note_IOOR -> "Note"
+      Note_length_denominator_IOOR -> "Note length denominator"
+      Tempo_IOOR -> "Tempo"
+      The_least_common_denominator_of_note_lengths_IOOR -> "The least common denominator of note lengths"
+      The_number_of_pitched_tracks_IOOR -> "The number of pitched tracks"
+      The_number_of_tracks_IOOR -> "The number of tracks"
+      Track_length_IOOR -> "Track length"
+      Velocity_IOOR -> "Velocity"
diff --git a/Composition/Keyboard.hs b/Composition/Keyboard.hs
new file mode 100644
--- /dev/null
+++ b/Composition/Keyboard.hs
@@ -0,0 +1,710 @@
+{-| ---
+Description: Functions that deal with keyboard instruments. ---
+ ---
+* Checking whether a set of notes is playable on the keyboard ---
+* Keyboard transcriptions ---
+-} ---
+module Composition.Keyboard (Keyboard (..), Playable (..), is_playable, keyboard, parse_playable, set_is_playable) where ---
+  import Composition.Errors ---
+  import Composition.Notes ---
+  import Composition.Score ---
+  import Composition.Theory ---
+  import Control.Lens.Combinators hiding (parts) ---
+  import Control.Monad.Except ---
+  import Control.Monad.Trans.Except ---
+  import Data.Char ---
+  import Data.Foldable as Foldable ---
+  import Data.Functor ---
+  import Data.List as List ---
+  import Data.Map.Strict as Map hiding (keys) ---
+  import Data.Maybe as Maybe ---
+  import Data.Ord ---
+  import Data.Set as Set ---
+  import Parser.Files ---
+  import Parser.Locations ---
+  import Parser.Parser ---
+  import Parser.Utilities ---
+  data Char_class = Delimiter_char Token | Invalid_char | Nonzero_nat_char Char | Whitespace_char | Zero_char ---
+  -- | Data structure for storing the left and right hand part of a chord or a piece. ---
+  data Keyboard t = Keyboard t t ---
+  data Notes_in = Notes_in (Set (Note Pitched)) (Set (Note Pitched)) ---
+  data Notes_in' = Notes_in' (Set (Note Pitched)) | Tie_in' (Set (Note Pitched)) ---
+  data Notes_out = Rest_out | Notes_out (Set (Note Pitched)) | Tie_out ---
+  type Parser = Parser' Token Error ---
+  -- | Data structure for describing playable note combinations. For example, [15] [3 10] means that two notes with an interval ---
+  -- of 15 semitones are playable only starting from D#/Eb and A#/Bb. ---
+  data Playable = Playable {intervals :: [Int], starting_keys :: Set Int} ---
+  data Token = Left_square_bracket_token | Newline_token | Positive_int_token Int | Right_square_bracket_token | Zero_token ---
+  type Tokeniser = Tokeniser' Char_class Token Error ---
+  instance Applicative Keyboard where ---
+    Keyboard f g <*> Keyboard left_hand right_hand = Keyboard (f left_hand) (g right_hand) ---
+    pure x = Keyboard x x ---
+  deriving instance Eq Char_class ---
+  deriving instance Eq Token ---
+  instance Foldable Keyboard where ---
+    foldr f x (Keyboard left_hand right_hand) = f left_hand (f right_hand x) ---
+  instance Functor Keyboard where ---
+    fmap f (Keyboard left_hand right_hand) = Keyboard (f left_hand) (f right_hand) ---
+  instance Traversable Keyboard where ---
+    traverse f (Keyboard left_hand right_hand) = Keyboard <$> f left_hand <*> f right_hand ---
+  classify_char :: Char -> Char_class ---
+  classify_char c = ---
+    case c of ---
+      '\n' -> Delimiter_char Newline_token ---
+      ' ' -> Whitespace_char ---
+      '0' -> Zero_char ---
+      _ | isDigit c && c /= '0' -> Nonzero_nat_char c ---
+      '[' -> Delimiter_char Left_square_bracket_token ---
+      ']' -> Delimiter_char Right_square_bracket_token ---
+      _ -> Invalid_char ---
+  construct_keys :: [Int] -> Either (Location -> Error) (Set Int) ---
+  construct_keys keys = ---
+    case construct_set keys of ---
+      Nothing -> Left Duplicate_keys ---
+      Just keys' -> Right keys' ---
+  crossing_middle_c :: Keyboard (Set (Note Pitched)) -> Int ---
+  crossing_middle_c (Keyboard l r) = crossing_middle_c' id Set.findMax l `max` crossing_middle_c' flip Set.findMin r ---
+  crossing_middle_c' :: ---
+    ( ---
+      ((Note Pitched -> Note Pitched -> Int) -> Note Pitched -> Note Pitched -> Int) -> ---
+      (Set (Note Pitched) -> Note Pitched) -> ---
+      Set (Note Pitched) -> ---
+      Int) ---
+  crossing_middle_c' maybeflip aggr notes = ---
+    (maybeflip distance_in_semitones (Pitched_note 4 C)) (aggr (Set.insert (Pitched_note 4 C) notes)) ---
+  delimiter_char :: Char_class -> Maybe Token ---
+  delimiter_char char_class = ---
+    case char_class of ---
+      Delimiter_char token -> Just token ---
+      _ -> Nothing ---
+  events_to_events_in :: [[Event' (Set (Note Pitched))]] -> [Event' Notes_in] ---
+  events_to_events_in events = events_to_events_in' ((<$>) (over #event_notes Notes_in') <$> events) ---
+  events_to_events_in' :: [[Event' (Notes_in')]] -> [Event' Notes_in] ---
+  events_to_events_in' events = ---
+    case () of ---
+      () | all Foldable.null events -> [] ---
+      () -> ---
+        let ---
+          len = minimum (view #event_length <$> head <$> events) ---
+          heads_and_tails = head_and_tail len <$> events in ---
+          ( ---
+            Event' (Foldable.foldr join_notes (Notes_in Set.empty Set.empty) (fst <$> heads_and_tails)) len : ---
+            events_to_events_in' (snd <$> heads_and_tails)) ---
+  filter_by_key :: (Eq t) => ([t] -> t) -> (u -> t) -> [u] -> [u] ---
+  filter_by_key fold_keys compute_key x = List.filter (\ y -> fold_keys (compute_key <$> x) == compute_key y) x ---
+  gen_hands :: [Playable] -> [Note Pitched] -> [Set (Note Pitched)] ---
+  gen_hands playable notes = ---
+    Set.empty : gen_hands' Set.empty notes where ---
+    gen_hands' :: Set (Note Pitched) -> [Note Pitched] -> [Set (Note Pitched)] ---
+    gen_hands' picked_notes potential_notes = ---
+      case potential_notes of ---
+        [] -> [] ---
+        note : potential_notes' -> ---
+          let ---
+            picked_notes' = Set.insert note picked_notes in ---
+            case is_acceptable playable picked_notes' of ---
+              False -> gen_hands' picked_notes potential_notes' ---
+              True -> picked_notes' : gen_hands' picked_notes' potential_notes' ---
+  gen_keyboard_confs :: [Note Pitched] -> [Keyboard (Set (Note Pitched))] ---
+  gen_keyboard_confs notes = Keyboard Set.empty (Set.fromList notes) : gen_keyboard_confs' Set.empty notes ---
+  gen_keyboard_confs' :: Set (Note Pitched) -> [Note Pitched] -> [Keyboard (Set (Note Pitched))] ---
+  gen_keyboard_confs' l r = ---
+    case r of ---
+      [] -> [] ---
+      note : r' -> ---
+        let ---
+          l' = Set.insert note l in ---
+          Keyboard l' (Set.fromList r') : gen_keyboard_confs' l' r' ---
+  hand_span :: Set (Note Pitched) -> Int ---
+  hand_span notes = ---
+    case Set.elems notes of ---
+      [] -> -1 ---
+      _ -> distance_in_semitones (minimum notes) (maximum notes) ---
+  head_and_tail :: Length_fraction -> [Event' (Notes_in')] -> (Notes_in, [Event' (Notes_in')]) ---
+  head_and_tail target_len events = ---
+    case events of ---
+      [] -> undefined ---
+      Event' notes len : events' -> ---
+        ( ---
+          transf_notes_in notes, ---
+          case () of ---
+            () | target_len == len -> events' ---
+            () -> Event' (notes_to_tie notes) (len - target_len) : events') ---
+  is_acceptable :: [Playable] -> Set (Note Pitched) -> Bool ---
+  is_acceptable playable notes = ---
+    case Set.elems notes of ---
+      [] -> True ---
+      note@(Pitched_note _ note_name) : notes' -> ---
+        any ---
+          (\ (Playable intervs starts) -> intervs == tointervs note notes' && any ((==) (semitones_from_c note_name)) starts) ---
+          playable ---
+  -- | Check if this configuration is playable on the keyboard. ---
+  is_playable :: [Playable] -> Keyboard (Set (Note Pitched)) -> Bool ---
+  is_playable playable k@(Keyboard l r) = ---
+    is_acceptable (mirror_playable <$> playable) l && is_acceptable playable r && not_crossing k ---
+  join_notes :: Notes_in -> Notes_in -> Notes_in ---
+  join_notes (Notes_in notes_0 ties_0) (Notes_in notes_1 ties_1) = ---
+    Notes_in (Set.union notes_0 notes_1) (Set.union ties_0 ties_1) ---
+  join_notes_out :: [Event' Notes_out] -> [Event_fraction Pitched] ---
+  join_notes_out events = ---
+    case events of ---
+      [] -> [] ---
+      Event' notes len : events' -> join_notes_out' (Event' (notes_to_set notes) len) events' ---
+  join_notes_out' :: Event_fraction Pitched -> [Event' Notes_out] -> [Event_fraction Pitched] ---
+  join_notes_out' (Event' notes_0 len_0) events = ---
+    case events of ---
+      [] -> [Event' notes_0 len_0] ---
+      Event' maybe_notes_1 len_1 : events' -> ---
+        case maybe_notes_1 of ---
+          Rest_out -> ---
+            case Set.elems notes_0 of ---
+              [] -> join_notes_out' (Event' Set.empty (len_0 + len_1)) events' ---
+              _ : _ -> Event' notes_0 len_0 : join_notes_out' (Event' Set.empty len_1) events' ---
+          Notes_out notes_1 -> Event' notes_0 len_0 : join_notes_out' (Event' notes_1 len_1) events' ---
+          Tie_out -> join_notes_out' (Event' notes_0 (len_0 + len_1)) events' ---
+  keyboard :: [Playable] -> Maybe String -> MIDI_instrument -> Score -> Either Error Score ---
+  keyboard playable header_instrument midi_instrument (Score {title = score_title, header, parts}) = ---
+    do ---
+      keyboard_parts <- traverse keyboard_part parts ---
+      Right (Score {title = score_title, header = keyboard_header, parts = keyboard_parts}) where ---
+    gen_variants :: Set (Note Pitched) -> [Keyboard (Set (Note Pitched))] ---
+    gen_variants notes = ---
+      List.filter ---
+        not_crossing ---
+        ( ---
+          Keyboard <$> ---
+          gen_hands (mirror_playable <$> playable) (Set.elems notes) <*> ---
+          gen_hands playable (reverse (Set.elems notes))) ---
+    keyb :: Keyboard (Set (Note Pitched)) -> [Event' Notes_in] -> Keyboard [Event' Notes_out] ---
+    keyb (Keyboard old_notes_lft old_notes_rght) events = ---
+      case events of ---
+        [] -> pure [] ---
+        Event' (Notes_in notes ties) len : events' -> ---
+          let ---
+            Keyboard lft rght = ---
+              head ---
+                (( ---
+                  filter_by_key maximum right_hand_note_count <$> ---
+                  filter_by_key minimum narrower_hand_span <$> ---
+                  filter_by_key minimum wider_hand_note_count <$> ---
+                  filter_by_key minimum wider_hand_span <$> ---
+                  filter_by_key minimum crossing_middle_c <$> ---
+                  filter_by_key maximum number_of_notes) ---
+                  (gen_variants notes)) ---
+            (cont_notes_arg_lft, cont_notes_res_lft) = ---
+              case Set.elems lft of ---
+                [] -> ---
+                  case Set.elems (Set.union old_notes_lft ties) of ---
+                    [] -> (Set.empty, Rest_out) ---
+                    _ : _ -> (old_notes_lft, Tie_out) ---
+                _ : _ -> (lft, Notes_out lft) ---
+            (cont_notes_arg_rght, cont_notes_res_rght) = ---
+              case Set.elems rght of ---
+                [] -> ---
+                  case Set.elems (Set.union old_notes_rght ties) of ---
+                    [] -> (Set.empty, Rest_out) ---
+                    _ : _ -> (old_notes_rght, Tie_out) ---
+                _ : _ -> (rght, Notes_out rght) in ---
+            ( ---
+              (:) <$> ---
+              ((\ n -> Event' n len) <$> Keyboard cont_notes_res_lft cont_notes_res_rght) <*> ---
+              keyb (Keyboard cont_notes_arg_lft cont_notes_arg_rght) events') ---
+    keyboard_header :: Map Header_field String ---
+    keyboard_header = ---
+      case header_instrument of ---
+        Nothing -> header ---
+        Just header_instrument' -> Map.insert Instrument header_instrument' header ---
+    keyboard_part :: Part -> Either Error Part ---
+    keyboard_part (Part {title = part_title, key, time, initial_position, tempo, stave_groups}) = ---
+      do ---
+        let tracks = remove_notational_information stave_groups ---
+        _ <- tracks_length tracks ---
+        Keyboard left_hand_track right_hand_track <- ---
+          traverse ---
+            (notate time initial_position) ---
+            ( ---
+              join_notes_out <$> ---
+              (keyb ---
+                (pure Set.empty) ---
+                (events_to_events_in ---
+                  (Maybe.mapMaybe ---
+                    (pitched_or_unpitched (\ (Track {events}) -> Just events) (\ _ -> Nothing)) ---
+                    (remove_notational_information stave_groups))))) ---
+        Right ---
+          (Part { ---
+            title = part_title, ---
+            key, ---
+            time, ---
+            initial_position, ---
+            tempo, ---
+            stave_groups = ---
+              [ ---
+                [ ---
+                  Pitched ---
+                    (Instrument_clefs_and_staves { ---
+                      instrument_name = "", ---
+                      midi_instrument, ---
+                      velocity = max_velocity, ---
+                      clefs_and_staves = ---
+                        [ ---
+                          Clef_and_stave { ---
+                            clef = Pitched_clef {clef_name = Treble, transposition = 0}, ---
+                            stave = One_track right_hand_track}, ---
+                          Clef_and_stave { ---
+                            clef = Pitched_clef {clef_name = Bass, transposition = 0}, ---
+                            stave = One_track left_hand_track}]})]]}) ---
+  mirror_key :: Int -> Int ---
+  mirror_key key = mod (4 - key) 12 ---
+  mirror_playable :: Playable -> Playable ---
+  mirror_playable (Playable intervals keys) = Playable (reverse intervals) (Set.fromList (mirror_key <$> Set.elems keys)) ---
+  nat_char :: Char_class -> Maybe Char ---
+  nat_char char_class = ---
+    case char_class of ---
+      Zero_char -> Just '0' ---
+      Nonzero_nat_char c -> Just c ---
+      _ -> Nothing ---
+  narrower_hand_span :: Keyboard (Set (Note Pitched)) -> Int ---
+  narrower_hand_span k = minimum (hand_span <$> k) ---
+  next_location :: Char_class -> Location -> Location ---
+  next_location char_class = ---
+    case char_class of ---
+      Delimiter_char Newline_token -> next_line ---
+      _ -> next_char ---
+  nonzero_nat_char :: Char_class -> Maybe Char ---
+  nonzero_nat_char char_class = ---
+    case char_class of ---
+      Nonzero_nat_char c -> Just c ---
+      _ -> Nothing ---
+  not_crossing :: Keyboard (Set (Note Pitched)) -> Bool ---
+  not_crossing (Keyboard l r) = ---
+    case (Set.elems l, Set.elems r) of ---
+      (_ : _, _ : _) -> Set.findMax l < Set.findMin r ---
+      _ -> True ---
+  notes_to_set :: Notes_out -> Set (Note Pitched) ---
+  notes_to_set maybe_notes = ---
+    case maybe_notes of ---
+      Rest_out -> Set.empty ---
+      Notes_out notes -> notes ---
+      Tie_out -> Set.empty ---
+  notes_to_tie :: Notes_in' -> Notes_in' ---
+  notes_to_tie maybe_notes = ---
+    case maybe_notes of ---
+      Notes_in' notes -> Tie_in' notes ---
+      Tie_in' notes -> Tie_in' notes ---
+  number_of_notes :: Keyboard (Set (Note Pitched)) -> Int ---
+  number_of_notes k = sum (Set.size <$> k) ---
+  parse_key :: Parser Int ---
+  parse_key = filter_parser ((>=) 11) (Is_out_of_range_with_location Key_IOOR) parse_nat ---
+  parse_keys :: Parser (Set Int) ---
+  parse_keys = fmap_filter_parser construct_keys (parse_list' parse_key) ---
+  parse_list' :: Parser t -> Parser [t] ---
+  parse_list' parse_t = parse_square_brackets (parse_many parse_t) ---
+  parse_nat :: Parser Int ---
+  parse_nat = parse_zero <+> parse_positive_int ---
+  parse_playable :: File_path -> ExceptT Error IO [Playable] ---
+  parse_playable file_path = ---
+    do ---
+      file <- read_file "key" readFile File_error file_path ---
+      except (fromJust (parse' classify_char next_location tokenise parse_playable' Parse_error file)) ---
+  parse_playable' :: Parser [Playable] ---
+  parse_playable' = parse_list Newline_token (Playable <$> parse_list' parse_positive_int <*> parse_keys) ---
+  parse_positive_int :: Parser Int ---
+  parse_positive_int = parse_token' positive_int_token ---
+  parse_square_brackets :: Parser t -> Parser t ---
+  parse_square_brackets = parse_brackets Left_square_bracket_token Right_square_bracket_token ---
+  parse_zero :: Parser Int ---
+  parse_zero = ---
+    do ---
+      parse_token Zero_token ---
+      return 0 ---
+  positive_int_token :: Token -> Maybe Int ---
+  positive_int_token token = ---
+    case token of ---
+      Positive_int_token i -> Just i ---
+      _ -> Nothing ---
+  right_hand_note_count :: Keyboard (Set (Note Pitched)) -> Int ---
+  right_hand_note_count (Keyboard _ r) = Set.size r ---
+  -- | Check if the set of notes is playable on the keyboard. ---
+  set_is_playable :: [Playable] -> Set (Note Pitched) -> Bool ---
+  set_is_playable playable notes = any (is_playable playable) (gen_keyboard_confs (Set.elems notes)) ---
+  tointervs :: Note Pitched -> [Note Pitched] -> [Int] ---
+  tointervs note_0 notes = ---
+    case notes of ---
+      [] -> [] ---
+      note_1 : notes' -> distance_in_semitones note_0 note_1 : tointervs note_1 notes' ---
+  tokenise :: Tokeniser () ---
+  tokenise = void (parse_many tokenise_1) ---
+  tokenise_1 :: Tokeniser () ---
+  tokenise_1 = tokenise_delimiter <+> tokenise_nat <+> tokenise_whitespace ---
+  tokenise_delimiter :: Tokeniser () ---
+  tokenise_delimiter = add_token (parse_token' delimiter_char) ---
+  tokenise_nat :: Tokeniser () ---
+  tokenise_nat = add_token (tokenise_zero <+> tokenise_positive_int) ---
+  tokenise_positive_int :: Tokeniser Token ---
+  tokenise_positive_int = ---
+    Positive_int_token <$> read <$> ((:) <$> parse_token' nonzero_nat_char <*> parse_many (parse_token' nat_char)) ---
+  tokenise_whitespace :: Tokeniser () ---
+  tokenise_whitespace = parse_token Whitespace_char ---
+  tokenise_zero :: Tokeniser Token ---
+  tokenise_zero = ---
+    do ---
+      parse_token Zero_char ---
+      return Zero_token ---
+  transf_notes_in :: Notes_in' -> Notes_in ---
+  transf_notes_in maybe_notes = ---
+    case maybe_notes of ---
+      Notes_in' notes -> Notes_in notes Set.empty ---
+      Tie_in' notes -> Notes_in Set.empty notes ---
+  wider_hand_span :: Keyboard (Set (Note Pitched)) -> Int ---
+  wider_hand_span k = maximum (hand_span <$> k) ---
+  wider_hand_note_count :: Keyboard (Set (Note Pitched)) -> Int ---
+  wider_hand_note_count k = Set.size (maximumBy (comparing hand_span) k) ---
+{- ---
+  calc_intervs :: Note Pitched -> [Note Pitched] -> [Int] ---
+  calc_intervs note notes = ---
+    case notes of ---
+      [] -> [] ---
+      note' : notes' -> distance_in_semitones note note' : calc_intervs note' notes' ---
+  cat_head_tail :: Length_fraction -> Notes_in -> [Event_out] -> [Event_out] ---
+  cat_head_tail len notes_in evs = ---
+    case (notes_in, evs) of ---
+      (Rest_in, []) -> [Event_out Rest_out len] ---
+      (Rest_in, Event_out Rest_out len' : evs') -> Event_out Rest_out (len + len') : evs' ---
+      (Rest_in, Event_out (Notes_out notes') len' : evs') -> Event_out Rest_out len : Event_out (Notes_out notes') len' : evs' ---
+      (Rest_in, Event_out Tie_out _ : _) -> undefined ---
+      (Notes_in notes, []) -> [Event_out (Notes_out notes) len] ---
+      (Notes_in notes, Event_out Rest_out len' : evs') -> Event_out (Notes_out notes) len : Event_out Rest_out len' : evs' ---
+      (Notes_in notes, Event_out (Notes_out notes') len' : evs') -> ---
+        Event_out (Notes_out notes) len : Event_out (Notes_out notes') len' : evs' ---
+      (Notes_in notes, Event_out Tie_out len' : evs') -> Event_out (Notes_out notes) (len + len') : evs' ---
+      (Tie_in _, []) -> [Event_out Tie_out len] ---
+      (Tie_in _, Event_out Rest_out len' : evs') -> Event_out Tie_out len : Event_out Rest_out len' : evs' ---
+      (Tie_in _, Event_out (Notes_out notes') len' : evs') -> Event_out Tie_out len : Event_out (Notes_out notes') len' : evs' ---
+      (Tie_in _, Event_out Tie_out len' : evs') -> Event_out Tie_out (len + len') : evs' ---
+  choose_next_chord :: [([Int], [Int])] -> Keyboard Old_notes -> Set (Note Pitched) -> Set (Note Pitched) -> Keyboard Notes_in ---
+  choose_next_chord acceptable_combinations old_notes potential_tied_notes potential_new_notes = ---
+      min_wider_hand_range = minimum (wider_hand_range <$> all_lrs_3) ---
+      all_lrs_4 = List.filter (\ var -> min_wider_hand_range == wider_hand_range var) all_lrs_3 ---
+      min_max_notes = minimum (max_notes <$> all_lrs_4) ---
+      all_lrs_5 = List.filter (\ var -> min_max_notes == max_notes var) all_lrs_4 ---
+      min_narrower_hand_range = minimum (narrower_hand_range <$> all_lrs_5) ---
+      all_lrs_6 = List.filter (\ var -> min_narrower_hand_range == narrower_hand_range var) all_lrs_5 ---
+      max_right_hand_notes = maximum (right_hand_notes <$> all_lrs_6) ---
+      all_lrs_7 = List.filter (\ var -> max_right_hand_notes == right_hand_notes var) all_lrs_6 in ---
+      find_res potential_tied_notes <$> old_notes <*> head all_lrs_7 ---
+  constr_events_in :: [Event_fraction Pitched] -> Events_in ---
+  constr_events_in events = ---
+    case events of ---
+      [] -> Empty_events_in ---
+      Event_fraction notes len : events' -> Construct_events_in (First_event_in (constr_notes_in notes) len) events' ---
+  constr_notes_in :: Set (Note Pitched) -> Notes_in ---
+  constr_notes_in notes = ---
+    case Set.elems notes of ---
+      [] -> Rest_in ---
+      _ -> Notes_in notes ---
+  constr_events_out :: Time -> Initial_position -> [Event_out] -> Either Error [Event Pitched] ---
+  constr_events_out time initial_position events = notate time initial_position (constr_events_out' events) ---
+  constr_events_out' :: [Event_out] -> [Event_fraction Pitched] ---
+  constr_events_out' events = ---
+    case events of ---
+      [] -> [] ---
+      [Event_out notes len] -> ---
+        [ ---
+          Event_fraction ---
+            (case notes of ---
+              Rest_out -> Set.empty ---
+              Notes_out notes' -> notes' ---
+              Tie_out -> undefined) ---
+            len] ---
+      Event_out notes_0 len_0 : Event_out notes_1 len_1 : events' -> ---
+        case (notes_0, notes_1) of ---
+          (Rest_out, Rest_out) -> constr_events_out' (Event_out Rest_out (len_0 + len_1) : events') ---
+          (Rest_out, Notes_out _) -> ---
+            Event_fraction Set.empty len_0 : constr_events_out' (Event_out notes_1 len_1 : events') ---
+          (Notes_out notes'_0, Rest_out) -> ---
+            Event_fraction notes'_0 len_0 : constr_events_out' (Event_out notes_1 len_1 : events') ---
+          (Notes_out notes'_0, Notes_out _) -> ---
+            Event_fraction notes'_0 len_0 : constr_events_out' (Event_out notes_1 len_1 : events') ---
+          (Notes_out _, Tie_out) -> constr_events_out' (Event_out notes_0 (len_0 + len_1) : events') ---
+          _ -> undefined ---
+-} ---
+{- ---
+  filter_new_notes :: Notes_in -> Maybe (Set (Note Pitched)) ---
+  filter_new_notes notes_in = ---
+    case notes_in of ---
+      New_notes_in notes -> Just notes ---
+      _ -> Nothing ---
+  filter_old_notes :: Notes_in -> Maybe (Set (Note Pitched)) ---
+  filter_old_notes notes_in = ---
+    case notes_in of ---
+      Old_notes_in notes -> Just notes ---
+      _ -> Nothing ---
+  filters :: Keyboard (Set (Note Pitched)) -> [Keyboard (Set (Note Pitched))] -> Keyboard (Set (Note Pitched)) ---
+  filters current_notes = _ ---
+-} ---
+{- ---
+  find_res :: Set (Note Pitched) -> Old_notes -> Set (Note Pitched) -> Notes_in ---
+  find_res potential_tied_notes old_notes new_notes = ---
+    case Set.null new_notes of ---
+      False -> Notes_in new_notes ---
+      True -> ---
+        case old_notes of ---
+          Old_notes_rest -> Rest_in ---
+          Old_notes_notes old_notes' -> ---
+            case Set.null (Set.intersection potential_tied_notes old_notes') of ---
+              False -> Tie_in old_notes' ---
+              True -> Rest_in ---
+  is_acceptable :: Playable -> [Note Pitched] -> Bool ---
+  is_acceptable acceptable_combinations notes = ---
+    case notes of ---
+      [] -> True ---
+      note : notes' -> any (is_acceptable' note notes') acceptable_combinations ---
+  is_acceptable' :: Note Pitched -> [Note Pitched] -> ([Int], [Int]) -> Bool ---
+  is_acceptable' note@(Pitched_note _ note_name) notes (intervs, strts) = ---
+    calc_intervs note notes == intervs && elem (semitones_from_c note_name) strts ---
+  keyb :: [Playable] -> Keyboard Old_notes -> [[Event' Notes_in]] -> Keyboard [Event' Notes_out] ---
+  keyb playable old_notes tracks = ---
+    case all ((==) Empty_events_in) tracks of ---
+      False -> ---
+        let ---
+          (len, cont_notes, new_notes, tracks') = head_and_tail tracks ---
+          next_notes = choose_next_chord acceptable_combinations old_notes cont_notes new_notes ---
+          next_events = keyb acceptable_combinations (transf_notes' <$> next_notes) tracks' in ---
+          cat_head_tail len <$> next_notes <*> next_events ---
+      True -> pure [] ---
+  keyboard :: File_path -> String -> Instrument Pitched -> File_path -> Directory -> String -> IO () ---
+  keyboard acceptable_combinations_file_path instrument_name midi_instrument score_file_path dir fname = ---
+    do ---
+      res <- runExceptT (keyboard_h acceptable_combinations_file_path instrument_name midi_instrument score_file_path) ---
+      case res of ---
+        Left err -> putStrLn (write_error err) ---
+        Right score -> write dir fname score ---
+  keyboard_h :: File_path -> String -> Instrument Pitched -> File_path -> ExceptT Error IO Score ---
+  keyboard_h acceptable_combinations_file_path instrument_name midi_instrument score_file_path = ---
+    do ---
+      acceptable_combinations <- parse_keyboard acceptable_combinations_file_path ---
+      score <- parse score_file_path ---
+      ExceptT (return (keyboard_help acceptable_combinations instrument_name midi_instrument score)) ---
+-} ---
+{- ---
+        let keyb_res = ---
+              keyb ---
+                acceptable_combinations ---
+                (pure Old_notes_rest) ---
+                (catMaybes ---
+                  (pitched_or_unpitched (\ (Track {events}) -> Just (constr_events_in events)) (return Nothing) <$> tracks)) ---
+        Keyboard left_hand_track right_hand_track <- traverse (constr_events_out time initial_position) keyb_res ---
+  max_notes :: Keyboard (Set (Note Pitched)) -> Int ---
+  max_notes k = maximum (Set.size <$> k) ---
+  narrower_hand_range :: Keyboard (Set (Note Pitched)) -> Int ---
+  narrower_hand_range k = minimum (hand_range <$> k) ---
+-} ---
+{- ---
+  notes_out_to_notes_0 :: Notes_out -> Set (Note Pitched) ---
+  notes_out_to_notes_0 notes_out = ---
+    case notes_out of ---
+      New_notes_out notes -> notes ---
+      Old_notes_out notes -> Tie ---
+      Rest_notes_out -> Set.empty ---
+-} ---
+{- ---
+  notes_out_to_notes_1 :: Notes' -> Notes Pitched ---
+  notes_out_to_notes_1 notes_out = ---
+    case notes_out of ---
+      Notes'_rest -> Notes (Set.empty) ---
+      Notes'_notes notes -> Notes notes ---
+      Notes'_tie _ -> Tie ---
+-} ---
+{- ---
+  notes_to_notes_in :: Set (Note Pitched) -> Notes Pitched -> (Notes_in, Set (Note Pitched)) ---
+  notes_to_notes_in prev_notes maybe_notes = ---
+    case maybe_notes of ---
+      Notes notes -> ---
+        ( ---
+          case Set.size notes of ---
+            0 -> Rest_notes_in ---
+            _ -> New_notes_in notes, ---
+          notes) ---
+      Tie -> (Old_notes_in prev_notes, prev_notes) ---
+-} ---
+{- ---
+  only_new :: Notes_in -> Set (Note Pitched) ---
+  only_new notes_in = ---
+    case notes_in of ---
+      Notes_in notes -> notes ---
+      _ -> Set.empty ---
+  only_ties :: Notes_in -> Set (Note Pitched) ---
+  only_ties notes_in = ---
+    case notes_in of ---
+      Tie_in notes -> notes ---
+      _ -> Set.empty ---
+  right_hand_notes :: Keyboard (Set (Note Pitched)) -> Int ---
+  right_hand_notes (Keyboard _ r) = Set.size r ---
+-} ---
+{- ---
+  select_new_notes :: Set (Note Pitched) -> Set (Note Pitched) -> Set (Note Pitched) -> Notes_out ---
+  select_new_notes old_notes current_notes new_notes = ---
+    case () of ---
+      () | Set.null new_notes -> ---
+        case () of ---
+          () | not (Set.null current_notes) && isSubsetOf current_notes old_notes -> Old_notes_out ---
+          () -> Rest_notes_out ---
+      _ -> New_notes_out new_notes ---
+  seq2 :: Set (Note Pitched) -> [Event Pitched] -> Either String [Simultaneous_in] ---
+  seq2 prev_notes evs = ---
+    case evs of ---
+      [] -> Right [] ---
+      ev : evs' -> ---
+        do ---
+          (ev2, nxt_notes) <- simultaneous_to_simultaneous_in prev_notes ev ---
+          evs2 <- seq2 nxt_notes evs' ---
+          Right (ev2 : evs2) ---
+-} ---
+{- ---
+  sequential_in_tail_to_sequential_in :: [Simultaneous_in] -> Sequential_in ---
+  sequential_in_tail_to_sequential_in sequential_in_tail = ---
+    case sequential_in_tail of ---
+      [] -> Empty_sequential_in ---
+      simultaneous_in : sequential_in_tail' -> Construct_sequential_in simultaneous_in sequential_in_tail' ---
+-} ---
+{- ---
+  sequential_out_to_sequential :: Sequential_out -> [Event Pitched] ---
+  sequential_out_to_sequential sequential_out = ---
+    case sequential_out of ---
+      Empty_sequential_out -> [] ---
+      Construct_sequential_out simultaneous_out sequential -> simultaneous_out_to_simultaneous simultaneous_out : sequential ---
+  sequential_to_sequential_in :: Old_notes -> [Event Pitched] -> Either String ([Event'], Old_notes) ---
+  sequential_to_sequential_in maybe_old_notes sequential = ---
+    case sequential of ---
+      [] -> Right ([], maybe_old_notes) ---
+      ev : sequential' -> ---
+        case ev of ---
+          Event maybe_new_notes len -> ---
+            case maybe_new_notes of ---
+              Notes new_notes -> ---
+                case Set.size new_notes of ---
+                  0 -> first ((:) (Event' Notes'_rest len)) <$> sequential_to_sequential_in Old_notes_rest sequential' ---
+                  _ -> ---
+                    ( ---
+                      first ((:) (Event' (Notes'_notes new_notes) len)) <$> ---
+                      sequential_to_sequential_in (Old_notes_notes new_notes) sequential') ---
+              Tie -> ---
+                case maybe_old_notes of ---
+                  Old_notes_rest -> ---
+                    first ((:) (Event' Notes'_rest len)) <$> sequential_to_sequential_in Old_notes_rest sequential' ---
+                  Old_notes_notes old_notes -> ---
+                    ( ---
+                      first ((:) (Event' (Notes'_tie old_notes) len)) <$> ---
+                      sequential_to_sequential_in (Old_notes_notes old_notes) sequential') ---
+          Tuplet rat evs -> ---
+            do ---
+              (evs', maybe_old_notes') <- sequential_to_sequential_in maybe_old_notes evs ---
+              (evs'', maybe_old_notes'') <- sequential_to_sequential_in maybe_old_notes' sequential' ---
+              Right (Tuplet' rat evs' : evs'', maybe_old_notes'') ---
+  simultaneous_out_to_simultaneous :: Event' -> Event Pitched ---
+  simultaneous_out_to_simultaneous ev = ---
+    case ev of ---
+      Event' notes_out len -> Event (notes_out_to_notes_1 notes_out) len ---
+      Tuplet' rat evs -> Tuplet rat (simultaneous_out_to_simultaneous <$> evs) ---
+-} ---
+{- ---
+  simultaneous_to_simultaneous_in :: Set (Note Pitched) -> Event Pitched -> Either String (Simultaneous_in, Set (Note Pitched)) ---
+  simultaneous_to_simultaneous_in prev_notes (Event notes len) = ---
+    do ---
+      check "Non-positive note length." (0 < len) ---
+      let (notes', notes'') = notes_to_notes_in prev_notes notes ---
+      Right (Simultaneous_in notes' len, notes'') ---
+  tail_notes :: Notes_in -> Notes_in ---
+  tail_notes notes_in = ---
+    case notes_in of ---
+      New_notes_in notes -> Old_notes_in notes ---
+      Old_notes_in notes -> Old_notes_in notes ---
+      Rest_notes_in -> Rest_notes_in ---
+-} ---
+{- ---
+  transf_notes' :: Notes_in -> Old_notes ---
+  transf_notes' notes_in = ---
+    case notes_in of ---
+      Rest_in -> Old_notes_rest ---
+      Notes_in notes -> Old_notes_notes notes ---
+      Tie_in notes -> Old_notes_notes notes ---
+  transf_old_pos :: Old_notes -> Set (Note Pitched) ---
+  transf_old_pos notes' = ---
+    case notes' of ---
+      Old_notes_rest -> Set.empty ---
+      Old_notes_notes notes -> notes ---
+  var_size :: Keyboard (Set (Note Pitched)) -> Int ---
+  var_size k = sum (Set.size <$> k) ---
+  compute_distance :: Keyboard (Set Note) -> Keyboard (Set Note) -> Semitones ---
+  compute_distance keyboard_notes_0 keyboard_notes_1 = sum (compute_distance' <$> keyboard_notes_0 <*> keyboard_notes_1) ---
+  compute_distance' :: Set Note -> Set Note -> Semitones ---
+  compute_distance' notes_0 notes_1 = sum (abs <$> (distance_in_semitones <$> elems notes_0 <*> elems notes_1)) ---
+  compute_distances_in_semitones :: [Note] -> [Semitones] ---
+  compute_distances_in_semitones notes = zipWith distance_in_semitones (init notes) (tail notes) ---
+  -- | The lowest and highest voices are always included. The algorithm attempts to include as many middle voices as possible ---
+  -- while keeping the score playable. ---
+  number_of_notes :: Keyboard (Set Note) -> Int ---
+  number_of_notes keyboard_notes = sum (fmap size keyboard_notes) ---
+  order :: Ord t => t -> t -> (t, t) ---
+  order x y = ---
+    case compare' x y of ---
+      LT' -> (x, y) ---
+      EQ' z -> (z, z) ---
+      GT' -> (y, x) ---
+  -- | Checks whether a set of notes is playable on the keyboard. ---
+  playable :: Set Note -> Bool ---
+  playable notes = any playable' (divide_between_hands (elems notes)) ---
+  playable' :: Keyboard (Set Note) -> Bool ---
+  playable' (Keyboard left_hand_notes right_hand_notes) = ---
+    playable_with_the_left_hand left_hand_notes && playable_with_the_right_hand right_hand_notes ---
+  playable_with_one_hand :: ---
+    (Set Note -> Maybe Note) -> (Semitones -> Semitones) -> ([Semitones] -> [Semitones]) -> Set Note -> Bool ---
+  playable_with_one_hand reference_note mirror_semitones_from_c mirror_distances_in_semitones notes = ---
+    case reference_note notes of ---
+      Nothing -> True ---
+      Just (Note _ note_name) -> ---
+        elem ---
+          (mirror_semitones_from_c (semitones_from_c note_name)) ---
+          (playable_with_the_right_hand_table (mirror_distances_in_semitones (compute_distances_in_semitones (elems notes)))) ---
+  playable_with_the_left_hand :: Set Note -> Bool ---
+  playable_with_the_left_hand = playable_with_one_hand lookupMax (\) reverse ---
+  playable_with_the_right_hand :: Set Note -> Bool ---
+  playable_with_the_right_hand = playable_with_one_hand lookupMin id id ---
+  range :: Keyboard (Set Note) -> Range ---
+  range (Keyboard left_hand_notes right_hand_notes) = ---
+    uncurry Range (swap (order (range' left_hand_notes) (range' right_hand_notes))) ---
+  range' :: Set Note -> Maybe Semitones ---
+  range' notes = distance_in_semitones <$> lookupMin notes <*> lookupMax notes ---
+  shape :: Keyboard (Set Note) -> Shape ---
+  shape (Keyboard left_hand_notes right_hand_notes) = ---
+    construct_shape (shape' id lookupMin reverse left_hand_notes) (shape' flip lookupMax id right_hand_notes) ---
+  shape' :: ---
+    ( ---
+      ((Note -> Note -> Semitones) -> Note -> Note -> Semitones) -> ---
+      (Set Note -> Maybe Note) -> ---
+      ([Note] -> [Note]) -> ---
+      Set Note -> ---
+      Maybe [Semitones]) ---
+  shape' flip_or_not get_reference_note reverse_or_not notes = ---
+    do ---
+      reference_note <- get_reference_note notes ---
+      Just (flip_or_not distance_in_semitones reference_note <$> reverse_or_not (elems (delete reference_note notes))) ---
+  wrong_order :: Maybe Note -> Maybe Note -> Maybe Semitones -> Keyboard (Set Note) -> Maybe Semitones ---
+  wrong_order left_hand_limit right_hand_limit if_distance_zero (Keyboard left_hand_notes right_hand_notes) = ---
+    let ---
+      f = wrong_order' if_distance_zero ---
+    in ---
+      max (f (lookupMax left_hand_notes) left_hand_limit) (f right_hand_limit (lookupMin right_hand_notes)) ---
+  wrong_order' :: Maybe Semitones -> Maybe Note -> Maybe Note -> Maybe Semitones ---
+  wrong_order' if_distance_zero maybe_note_0 maybe_note_1 = ---
+    do ---
+      note_0 <- maybe_note_0 ---
+      note_1 <- maybe_note_1 ---
+      let distance = distance_in_semitones note_0 note_1 ---
+      case compare distance 0 of ---
+        LT -> Just (negate distance) ---
+        EQ -> if_distance_zero ---
+        GT -> Nothing ---
+  wrong_stave :: Keyboard (Set Note) -> Maybe Semitones ---
+  wrong_stave = wrong_order (Just (Note 4 C)) (Just (Note 4 C)) Nothing ---
+-} ---
diff --git a/Composition/Lilypond.hs b/Composition/Lilypond.hs
--- a/Composition/Lilypond.hs
+++ b/Composition/Lilypond.hs
@@ -1,461 +1,394 @@
---------------------------------------------------------------------------------------------------------------------------------
-{-# OPTIONS_GHC -Wall #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-|
-Description: A module for generating Lilypond files.
-
-This module contains the data structures and the function for generating Lilypond files from note sequences.
--}
-module Composition.Lilypond (
-  Basic_clef (..),
-  Bracket (..),
-  Clef (..),
-  Field (..),
-  Instrument_stave (..),
-  Part (..),
-  Part_header_field_name (..),
-  Score (..),
-  Score_header_field_name (..),
-  Stave (..),
-  lilypond) where
-  import Composition.Notes (
-    Accidental (..),
-    Natural_note_name (..),
-    Note (..),
-    Note_name (..),
-    Note_name' (..),
-    Rat,
-    Simultaneous (..),
-    Time (..),
-    Time_and_position (..),
-    deconstruct_note_name,
-    measure_length,
-    sequential_length,
-    simultaneous_length,
-    subdivision)
-  import Control.Monad (join)
-  import Data.Char (isPrint)
-  import Data.Fixed (mod')
-  import Data.List (intercalate)
-  import Data.Maybe (catMaybes)
-  import Data.Ratio ((%), denominator, numerator)
-  import System.Process (callCommand)
-  -- | Basic clefs without octave transposition.
-  data Basic_clef = Sub_bass | Bass | Baritone_F | Baritone_C | Tenor | Alto | Soprano | Mezzosoprano | Treble | French
-  -- | Staves can be separate from other staves or grouped with some of the other staves in a bracket. The curly bracket groups
-  -- staves of the same instrument, for example, the left- and right-hand part for the keyboard. The square bracket groups
-  -- several instruments in the same family, for example, the strings.
-  data Bracket = Curly_bracket (Maybe String) [Stave] | Single Instrument_stave | Square_bracket [Instrument_stave]
-  -- | Clefs with octave transposition. The second argument indicates the number of octaves by which the clef is transposed.
-  data Clef = Clef Basic_clef Int
-  type Err = Either String
-  -- | The type that generalises score and part header fields.
-  data Field field_name = Field field_name String
-  -- | A stave with optional instrument specification.
-  data Instrument_stave = Instrument_stave (Maybe String) Stave
-  -- | Each part can have a different time signature, tempo and instrumentation. The first argument is the list of header fields
-  -- that may include, for example, the title of the part. The second argument is the list of accidentals. For example, the key
-  -- signature of C minor would be @[E_flat, A_flat, B_flat]@.
-  data Part = Part [Field Part_header_field_name] [Note_name] Time_and_position [Bracket]
-  -- | Lilypond header fields that apply to only one part of the whole document.
-  data Part_header_field_name = Opus | Piece
-  -- | The first argument is the list of score header fields that may include, for example, the composer, the dedication, the
-  -- instrument, the subtitle and the title.
-  data Score = Score [Field Score_header_field_name] [Part]
-  -- | Lilypond header fields that apply to the whole document.
-  data Score_header_field_name =
-    Arranger | Composer | Copyright | Dedication | Instrument | Meter | Poet | Subsubtitle | Subtitle | Tagline | Title
-  -- | A stave consists of one homorhythmic note sequence. Heterorhythmic voices have to be notated on separate staves.
-  data Stave = Stave Clef [Simultaneous]
-  deriving instance Eq Part_header_field_name
-  deriving instance Eq Score_header_field_name
-  deriving instance Show Basic_clef
-  deriving instance Show Bracket
-  deriving instance Show Clef
-  deriving instance Show field_name => Show (Field field_name)
-  deriving instance Show Instrument_stave
-  deriving instance Show Part
-  deriving instance Show Part_header_field_name
-  deriving instance Show Score
-  deriving instance Show Score_header_field_name
-  deriving instance Show Stave
-  all_different :: Eq a => [a] -> Bool
-  all_different x =
-    case x of
-      [] -> True
-      y : z -> all ((/=) y) z && all_different z
-  bracket_length :: Bracket -> Err (Maybe Rat)
-  bracket_length bracket =
-    case bracket of
-      Curly_bracket _ staves -> check_lengths (stave_length <$> staves)
-      Single instrument_stave -> Right (Just (instrument_stave_length instrument_stave))
-      Square_bracket instrument_staves -> check_lengths (instrument_stave_length <$> instrument_staves)
-  check :: String -> Bool -> Err ()
-  check err condition =
-    case condition of
-      False -> Left err
-      True -> Right ()
-  check_bracket_lengths :: [Bracket] -> Err ()
-  check_bracket_lengths brackets =
-    do
-      lengths <- traverse bracket_length brackets
-      _ <- check_lengths (catMaybes lengths)
-      Right ()
-  check_lengths :: [Rat] -> Err (Maybe Rat)
-  check_lengths lengths =
-    case lengths of
-      [] -> Right Nothing
-      len : lengths' ->
-        do
-          check "Stave length mismatch." (all ((==) len) lengths')
-          Right (Just len)
-  check_range :: Ord t => String -> t -> t -> t -> Err ()
-  check_range typ min_t max_t x = check (typ ++ " out of range.") (min_t <= x && max_t >= x)
-  from_right :: Either t u -> u
-  from_right x =
-    case x of
-      Left _ -> undefined
-      Right y -> y
-  instrument_stave_length :: Instrument_stave -> Rat
-  instrument_stave_length (Instrument_stave _ stave) = stave_length stave
-  is_power_of_two :: Int -> Bool
-  is_power_of_two i =
-    case i of
-      1 -> True
-      _ -> even i && is_power_of_two (div i 2)
-  -- | Encodes the score in Lilypond format, writes it to the specified file and generates the pdf by calling lilypond.
-  lilypond :: String -> Score -> IO ()
-  lilypond file_name score =
-    do
-      let file_name_ly = file_name ++ ".ly"
-      case write_score score of
-        Left err -> putStrLn ("Lilypond error. " ++ err)
-        Right score' ->
-          do
-            writeFile file_name_ly score'
-            callCommand ("lilypond" ++ " " ++ file_name_ly)
-  lg :: Int -> Int
-  lg i =
-    case i of
-      1 -> 0
-      _ -> 1 + lg (div i 2)
-  max_denominator :: Int
-  max_denominator = 2 ^ (negate min_lg)
-  min_length :: Rat
-  min_length = 1 % max_denominator
-  min_lg :: Integer
-  min_lg = -7
-  split :: Time -> Rat -> Rat -> [(Rat, Rat)]
-  split time position len =
-    let
-      len' = measure_length time - position
-      position' = mod' position (measure_length (subdivision time))
-    in
-      case len > len' of
-        False -> [(position', len)]
-        True -> (position', len') : split time 0 (len - len')
-  stave_length :: Stave -> Rat
-  stave_length (Stave _ sequential) = sequential_length sequential
-  write_accidentals :: [Note_name] -> Err String
-  write_accidentals accidentals =
-    do
-      accidentals' <- traverse (write_key_accidental (deconstruct_note_name <$> accidentals)) [C_natural .. B_natural]
-      Right ("\\key" ++ " " ++ write_note_name C ++ " " ++ "#" ++ "`" ++ write_round (intercalate " " accidentals'))
-  write_angular :: String -> String
-  write_angular = write_brackets "<" ">"
-  write_angular_2 :: String -> String
-  write_angular_2 = write_brackets "<<" ">>"
-  write_bar_line :: String
-  write_bar_line = "\\bar" ++ " " ++ "\"|.\""
-  write_basic_clef :: Basic_clef -> String
-  write_basic_clef basic_clef =
-    case basic_clef of
-      Sub_bass -> "subbass"
-      Bass -> "bass"
-      Baritone_F -> "baritonevarF"
-      Baritone_C -> "baritone"
-      Tenor -> "tenor"
-      Alto -> "alto"
-      Soprano -> "soprano"
-      Mezzosoprano -> "mezzosoprano"
-      Treble -> "violin"
-      French -> "french"
-  write_bracket :: Time_and_position -> Bracket -> Err String
-  write_bracket time_and_initial_position bracket =
-    case bracket of
-      Curly_bracket instrument staves ->
-        do
-          instrument' <- write_instrument instrument
-          staves' <- traverse (write_stave time_and_initial_position Nothing) staves
-          Right ("\\new" ++ " " ++ "PianoStaff" ++ " " ++ instrument' ++ " " ++ write_angular_2 (intercalate " " staves'))
-      Single instrument_stave -> write_instrument_stave time_and_initial_position instrument_stave
-      Square_bracket instrument_staves ->
-        do
-          instrument_staves' <- traverse (write_instrument_stave time_and_initial_position) instrument_staves
-          Right ("\\new" ++ " " ++ "StaffGroup" ++ " " ++ write_angular_2 (intercalate " " instrument_staves'))
-  write_brackets :: String -> String -> String -> String
-  write_brackets left_bracket right_bracket x = left_bracket ++ x ++ right_bracket
-  write_char :: Char -> Err String
-  write_char c =
-    do
-      check "Invalid character." (isPrint c && not (elem c ['\t', '\v', '\f', '\r']))
-      Right
-        (case c of
-          '\n' -> "\\n"
-          '"' -> "\""
-          '\\' -> "\\\\"
-          _ -> [c])
-  write_clef :: Clef -> String
-  write_clef clef = "\\clef" ++ " " ++ from_right (write_quotes (write_clef' clef))
-  write_clef' :: Clef -> String
-  write_clef' (Clef basic_clef octave) = write_basic_clef basic_clef ++ write_clef_octave octave
-  write_clef_octave :: Int -> String
-  write_clef_octave octave =
-    case compare octave 0 of
-      LT -> "_" ++ write_clef_octave' (negate octave)
-      EQ -> ""
-      GT -> "^" ++ write_clef_octave' octave
-  write_clef_octave' :: Int -> String
-  write_clef_octave' octave = show (8 * octave - 1)
-  write_complex_length :: Time -> Rat -> Rat -> [String]
-  write_complex_length time position len = split time position len >>= write_simple_or_complex_length (subdivision time)
-  write_curly :: String -> String
-  write_curly = write_brackets "{" "}"
-  write_denominator :: Int -> Err String
-  write_denominator den =
-    do
-      check_range "Time signature denominator" 1 max_denominator den
-      check "Time signature denominator not a power of two." (is_power_of_two den)
-      Right (show den)
-  write_eq :: String -> String -> String
-  write_eq x y = x ++ " " ++ "=" ++ " " ++ y
-  write_field :: (field_name -> String) -> Field field_name -> Err String
-  write_field write_field_name (Field field_name value) = write_eq (write_field_name field_name) <$> write_quotes value
-  write_header :: Eq field_name => (field_name -> String) -> [Field field_name] -> Err String
-  write_header write_field_name fields =
-    write_maybe
-      (write_header' write_field_name)
-      (case fields of
-        [] -> Nothing
-        _ -> Just fields)
-  write_header' :: Eq field_name => (field_name -> String) -> [Field field_name] -> Err String
-  write_header' write_field_name fields =
-    do
-      check "Conflicting header fields." (all_different ((\(Field field_name _) -> field_name) <$> fields))
-      fields' <- traverse (write_field write_field_name) fields
-      Right ("\\header" ++ " " ++ write_curly (intercalate " " fields'))
-  write_initial_position :: Time -> Rat -> Err [String]
-  write_initial_position time initial_position =
-    do
-      check_range "Initial position" 0 (measure_length time - min_length) initial_position
-      Right
-        (case initial_position of
-          0 -> []
-          _ ->
-            [
-              "\\partial" ++
-              " " ++
-              show max_denominator ++
-              "*" ++
-              show (numerator ((measure_length time - initial_position) / min_length))])
-  write_instrument :: Maybe String -> Err String
-  write_instrument = write_maybe write_instrument'
-  write_instrument' :: String -> Err String
-  write_instrument' instrument =
-    do
-      instrument' <- write_quotes instrument
-      Right ("\\with" ++ " " ++ write_curly (write_eq "instrumentName" instrument'))
-  write_instrument_stave :: Time_and_position -> Instrument_stave -> Err String
-  write_instrument_stave time_and_initial_position (Instrument_stave instrument stave) =
-    write_stave time_and_initial_position instrument stave
-  write_key_accidental :: [Note_name'] -> Natural_note_name -> Err String
-  write_key_accidental accidentals natural_note_name =
-    do
-      let accidentals' = filter (\(Note_name' natural_note_name' _) -> natural_note_name == natural_note_name') accidentals
-      check "Conflicting accidentals in key signature." (2 > length accidentals')
-      Right
-        (write_round
-          (
-            show (fromEnum natural_note_name) ++
-            " " ++
-            "." ++
-            " " ++
-            "," ++
-            write_key_accidental'
-              (case accidentals of
-                [Note_name' _ accidental] -> accidental
-                _ -> Natural)))
-  write_key_accidental' :: Accidental -> String
-  write_key_accidental' accidental =
-    case accidental of
-      Flat -> "FLAT"
-      Natural -> "NATURAL"
-      Sharp -> "SHARP"
-  write_key_and_time :: [Note_name] -> Time_and_position -> Err String
-  write_key_and_time accidentals (Time_and_position time initial_position) =
-    do
-      accidentals' <- write_accidentals accidentals
-      time' <- write_time time
-      initial_position' <- write_initial_position time initial_position
-      Right
-        (write_eq
-          "Key_and_time"
-          (write_curly (intercalate " " ([accidentals', "\\numericTimeSignature"] ++ initial_position' ++ [time']))))
-  write_language :: String
-  write_language = "\\include" ++ " " ++ from_right (write_quotes "english.ly")
-  write_maybe :: (t -> Err String) -> Maybe t -> Err String
-  write_maybe write_t maybe_x =
-    case maybe_x of
-      Nothing -> Right ""
-      Just x -> write_t x
-  write_natural_note_name :: Natural_note_name -> String
-  write_natural_note_name natural_note_name =
-    case natural_note_name of
-      C_natural -> "c"
-      D_natural -> "d"
-      E_natural -> "e"
-      F_natural -> "f"
-      G_natural -> "g"
-      A_natural -> "a"
-      B_natural -> "b"
-  write_note :: Note -> String
-  write_note (Note octave note_name) = write_note_name note_name ++ write_note_octave octave
-  write_note_name :: Note_name -> String
-  write_note_name note_name =
-    let
-      Note_name' natural_note_name accidental = deconstruct_note_name note_name
-    in
-      write_natural_note_name natural_note_name ++ write_note_name_accidental accidental
-  write_note_name_accidental :: Accidental -> String
-  write_note_name_accidental accidental =
-    case accidental of
-      Flat -> "f"
-      Natural -> ""
-      Sharp -> "s"
-  write_note_octave :: Int -> String
-  write_note_octave octave =
-    case compare octave 3 of
-      LT -> replicate (3 - octave) ','
-      EQ -> ""
-      GT -> replicate (octave - 3) '\''
-  write_notes :: [Note] -> String
-  write_notes notes =
-    case notes of
-      [note] -> write_note note
-      _ -> write_angular (intercalate " " (write_note <$> notes))
-  write_numerator :: [Int] -> Err String
-  write_numerator num =
-    do
-      check "Invalid time signature numerator." (all ((<) 1) num)
-      Right (show (product num))
-  write_option :: String -> String -> String
-  write_option option value = "#" ++ write_round ("ly:set-option" ++ " " ++ "'" ++ option ++ " " ++ value)
-  write_options :: String
-  write_options = intercalate " " [write_option "delete-intermediate-files" "#t", write_option "no-point-and-click" "#t"]
-  write_part :: Part -> Err String
-  write_part (Part header accidentals time_and_initial_position brackets) =
-    do
-      header' <- write_header write_part_header_field_name header
-      key_and_time <- write_key_and_time accidentals time_and_initial_position
-      check_bracket_lengths brackets
-      brackets' <- traverse (write_bracket time_and_initial_position) brackets
-      Right
-        (key_and_time ++ " " ++ "\\score" ++ " " ++ write_curly (header' ++ " " ++ write_angular_2 (intercalate " " brackets')))
-  write_part_header_field_name :: Part_header_field_name -> String
-  write_part_header_field_name field_name =
-    case field_name of
-      Opus -> "opus"
-      Piece -> "piece"
-  write_quotes :: String -> Err String
-  write_quotes text =
-    do
-      text' <- traverse write_char text
-      Right (write_brackets "\"" "\"" (join text'))
-  write_round :: String -> String
-  write_round = write_brackets "(" ")"
-  write_score :: Score -> Err String
-  write_score (Score header parts) =
-    do
-      header' <-
-        write_header
-        write_score_header_field_name
-        (case any (\(Field field_name _) -> field_name == Tagline) header of
-          False -> Field Tagline "" : header
-          True -> header)
-      parts' <- traverse write_part parts
-      Right (write_options ++ " " ++ write_language ++ " " ++ header' ++ " " ++ intercalate " " parts')
-  write_score_header_field_name :: Score_header_field_name -> String
-  write_score_header_field_name field_name =
-    case field_name of
-      Arranger -> "arranger"
-      Composer -> "composer"
-      Copyright -> "copyright"
-      Dedication -> "dedication"
-      Instrument -> "instrument"
-      Meter -> "meter"
-      Poet -> "poet"
-      Subsubtitle -> "subsubtitle"
-      Subtitle -> "subtitle"
-      Tagline -> "tagline"
-      Title -> "title"
-  write_sequential :: Time -> Rat -> [Simultaneous] -> Err [String]
-  write_sequential time position sequential =
-    case sequential of
-      [] -> Right []
-      simultaneous : sequential' ->
-        (
-          (:) <$>
-          write_simultaneous time position simultaneous <*>
-          write_sequential time (mod' (position + simultaneous_length simultaneous) (measure_length time)) sequential')
-  write_simple_length :: Rat -> Maybe String
-  write_simple_length len =
-    case is_power_of_two (1 + numerator len) && numerator len < 2 * denominator len of
-      False -> Nothing
-      True ->
-        let
-          dots = lg (1 + numerator len) - 1
-        in
-          Just (show ((2 :: Integer) ^ (lg (denominator len) - dots)) ++ replicate dots '.')
-  write_simple_or_complex_length :: Time -> (Rat, Rat) -> [String]
-  write_simple_or_complex_length time (position, len) =
-    case write_simple_length len of
-      Nothing -> write_complex_length time position len
-      Just len' -> [len']
-  write_simultaneous :: Time -> Rat -> Simultaneous -> Err String
-  write_simultaneous time position (Simultaneous notes len) =
-    do
-      check "Non-positive note length." (0 < len)
-      check "Tuplets are not supported." (is_power_of_two (denominator len))
-      check ("Notes shorter than 1/" ++ show max_denominator ++ " are not supported.") (max_denominator >= denominator len)
-      let lengths = write_complex_length time position len
-      Right
-        (case notes of
-          [] -> intercalate " " ((++) "r" <$> lengths)
-          _ -> write_notes notes ++ intercalate "~" lengths)
-  write_stave :: Time_and_position -> Maybe String -> Stave -> Err String
-  write_stave (Time_and_position time initial_position) instrument (Stave clef sequential) =
-    do
-      instrument' <- write_instrument instrument
-      sequential' <- write_sequential time initial_position sequential
-      Right
-        (
-          "\\new" ++
-          " " ++
-          "Staff" ++
-          " " ++
-          instrument' ++
-          " " ++
-          write_curly
-            ("\\Key_and_time" ++ " " ++ write_clef clef ++ " " ++ intercalate " " sequential' ++ " " ++ write_bar_line))
-  write_time :: Time -> Err String
-  write_time time =
-    do
-      time' <- write_time' time
-      Right ("\\time" ++ " " ++ time')
-  write_time' :: Time -> Err String
-  write_time' (Time num den) =
-    do
-      num' <- write_numerator num
-      den' <- write_denominator den
-      Right (num' ++ "/" ++ den')
---------------------------------------------------------------------------------------------------------------------------------
+{-| ---
+Description: Generating Lilypond files. ---
+ ---
+* Generating Lilypond files ---
+-} ---
+module Composition.Lilypond (lilypond) where ---
+  import Composition.Errors ---
+  import Composition.Notes ---
+  import Composition.Score ---
+  import Composition.Theory ---
+  import Composition.Time ---
+  import Control.Lens.Combinators hiding (index, parts) ---
+  import Control.Lens.Operators ---
+  import Control.Monad.Except ---
+  import Control.Monad.State.Strict ---
+  import Control.Monad.Trans.Except ---
+  import Data.Bifunctor ---
+  import Data.Char ---
+  import Data.Foldable ---
+  import Data.Generics.Labels () ---
+  import Data.List ---
+  import Data.Map.Strict as Map ---
+  import Data.Set as Set ---
+  import GHC.Generics ---
+  import GHC.Real ---
+  import Parser.Files ---
+  import Parser.Utilities ---
+  data Header_field' = Header_field' String String ---
+  data State = State {state_notes :: Bool, state_position :: Position} ---
+  deriving instance Generic Composition.Lilypond.State ---
+  deriving instance Show Header_field' ---
+  check_char :: Char -> Either Error () ---
+  check_char c = check Invalid_character_in_text (elem c " !#$%&'()*+,-./:;<=>?@[]^_`{|}~" || isDigit c || isLetter c) ---
+  convert_header_field :: (Header_field, String) -> Header_field' ---
+  convert_header_field (name, value) = Header_field' (write_header_field_name name) value ---
+  convert_events_to_pitched :: Note Pitched -> [Event note_type] -> [Event Pitched] ---
+  convert_events_to_pitched unpitched_note = (<$>) convert_event_to_pitched where ---
+    convert_event_to_pitched :: Event note_type -> Event Pitched ---
+    convert_event_to_pitched event = ---
+      case event of ---
+        Event notes len -> Event (convert_notes_to_pitched notes) len ---
+        Triplet events -> Triplet (convert_events_to_pitched unpitched_note events) ---
+    convert_note_to_pitched :: Note note_type -> Note Pitched ---
+    convert_note_to_pitched note = ---
+      case note of ---
+        Pitched_note _ _ -> note ---
+        Unpitched_note -> unpitched_note ---
+    convert_notes_to_pitched :: Notes note_type -> Notes Pitched ---
+    convert_notes_to_pitched maybe_notes = ---
+      case maybe_notes of ---
+        Notes notes -> Notes (Set.mapMonotonic convert_note_to_pitched notes) ---
+        Tie -> Tie ---
+  delete_default_tagline :: Header_field' ---
+  delete_default_tagline = Header_field' "tagline" "" ---
+  -- | Encodes the score in Lilypond format and writes it to the specified file. ---
+  lilypond :: File_path -> Score -> ExceptT Error IO () ---
+  lilypond file_path score = ---
+    do ---
+      written_score <- except (write_score score) ---
+      write_file "ly" writeFile File_error file_path written_score ---
+  write_accidental :: Accidental -> String ---
+  write_accidental accidental = ---
+    case accidental of ---
+      Flat -> "f" ---
+      Natural -> "" ---
+      Sharp -> "s" ---
+  write_angular_brackets :: String -> String ---
+  write_angular_brackets = write_brackets "<" ">" ---
+  write_angular_brackets_2 :: String -> String ---
+  write_angular_brackets_2 = write_brackets "<<" ">>" ---
+  write_basic_length :: Basic_length -> String ---
+  write_basic_length len = show (denominator (basic_length_to_fraction len)) ---
+  write_brackets :: String -> String -> String -> String ---
+  write_brackets left_bracket right_bracket x = left_bracket <> x <> right_bracket ---
+  write_clef :: Clef note_type -> String ---
+  write_clef clef = ---
+    case clef of ---
+      Percussion_clef -> "percussion" ---
+      Pitched_clef {clef_name, transposition} -> write_clef_name clef_name <> write_clef_transposition transposition ---
+  write_clef_name :: Clef_name -> String ---
+  write_clef_name clef_name = ---
+    case clef_name of ---
+      Subbass -> "subbass" ---
+      Bass -> "bass" ---
+      Baritone_F -> "baritonevarF" ---
+      Baritone_C -> "baritone" ---
+      Tenor -> "tenor" ---
+      Alto -> "alto" ---
+      Mezzosoprano -> "mezzosoprano" ---
+      Soprano -> "soprano" ---
+      Treble -> "treble" ---
+      French -> "french" ---
+  write_clef_transposition :: Steps -> String ---
+  write_clef_transposition transposition = ---
+    case compare transposition 0 of ---
+      LT -> write_clef_transposition' "_" (negate transposition) ---
+      EQ -> "" ---
+      GT -> write_clef_transposition' "^" transposition ---
+  write_clef_transposition' :: String -> Steps -> String ---
+  write_clef_transposition' index transposition = index <> show transposition ---
+  write_curly_brackets :: String -> String ---
+  write_curly_brackets = write_brackets "{" "}" ---
+  write_dot :: Dot -> Char ---
+  write_dot Dot = '.' ---
+  write_end_bar_line :: String ---
+  write_end_bar_line = "\\bar" <-> "\"|.\"" ---
+  write_eq :: String -> String -> String ---
+  write_eq x y = x <-> "=" <-> y ---
+  write_header :: [Header_field'] -> Either Error String ---
+  write_header header = ---
+    do ---
+      written_header_fields <- traverse write_header_field header ---
+      Right ("\\header" <-> write_curly_brackets (intercalate " " written_header_fields)) ---
+  write_header_field :: Header_field' -> Either Error String ---
+  write_header_field (Header_field' name value) = write_eq name <$> write_quotes value ---
+  write_header_field_name :: Header_field -> String ---
+  write_header_field_name name = ---
+    case name of ---
+      Composer -> "composer" ---
+      Instrument -> "instrument" ---
+      Subtitle -> "subtitle" ---
+  write_key :: Key -> Either Error String ---
+  write_key key = ---
+    do ---
+      note_names <- ---
+        traverse ---
+          (write_key_note_name (fromListWith (<>) (second return <$> deconstruct_note_name <$> Set.elems key))) ---
+          (enumFromTo C_natural B_natural) ---
+      Right (write_note_name C <-> "#" <> "`" <> write_round_brackets (intercalate " " note_names)) ---
+  write_key_accidental :: Accidental -> String ---
+  write_key_accidental accidental = ---
+    case accidental of ---
+      Flat -> "FLAT" ---
+      Natural -> "NATURAL" ---
+      Sharp -> "SHARP" ---
+  write_key_natural_note_name :: Natural_note_name -> String ---
+  write_key_natural_note_name natural_note_name = show (fromEnum natural_note_name) ---
+  write_key_note_name :: Map Natural_note_name [Accidental] -> Natural_note_name -> Either Error String ---
+  write_key_note_name key natural_note_name = ---
+    case Map.lookup natural_note_name key of ---
+      Nothing -> write_key_note_name' natural_note_name Natural ---
+      Just accidentals' -> ---
+        case accidentals' of ---
+          [accidental] -> write_key_note_name' natural_note_name accidental ---
+          _ -> Left Conflicting_key_signature ---
+  write_key_note_name' :: Natural_note_name -> Accidental -> Either Error String ---
+  write_key_note_name' natural_note_name accidental = ---
+    Right ---
+      (write_round_brackets (write_key_natural_note_name natural_note_name <-> "." <-> "," <> write_key_accidental accidental)) ---
+  write_language :: Either Error String ---
+  write_language = ---
+    do ---
+      written_language <- write_quotes "english.ly" ---
+      Right ("\\include" <-> written_language) ---
+  write_length :: Length -> String ---
+  write_length (Length len dots) = write_basic_length len <> (write_dot <$> dots) ---
+  write_natural_note_name :: Natural_note_name -> String ---
+  write_natural_note_name natural_note_name = ---
+    case natural_note_name of ---
+      C_natural -> "c" ---
+      D_natural -> "d" ---
+      E_natural -> "e" ---
+      F_natural -> "f" ---
+      G_natural -> "g" ---
+      A_natural -> "a" ---
+      B_natural -> "b" ---
+  write_note :: Note Pitched -> String ---
+  write_note (Pitched_note octave note_name) = write_note_name note_name <> write_octave octave ---
+  write_note_name :: Note_name -> String ---
+  write_note_name note_name = ---
+    let ---
+      (natural_note_name, accidental) = deconstruct_note_name note_name ---
+    in ---
+      write_natural_note_name natural_note_name <> write_accidental accidental ---
+  write_notes :: Notes Pitched -> StateT Composition.Lilypond.State (Either Error) (Bool, String) ---
+  write_notes maybe_notes = ---
+    case maybe_notes of ---
+      Notes notes -> ---
+        do ---
+          #state_notes .= not (Set.null notes) ---
+          return ---
+            ( ---
+              False, ---
+              case Set.elems notes of ---
+                [] -> "r" ---
+                notes' -> write_angular_brackets (intercalate " " (write_note <$> notes'))) ---
+      Tie -> ---
+        do ---
+          -- Composition.Lilypond.State {state_notes} <- get ---
+          notes <- use #state_notes ---
+          return ---
+            ( ---
+              notes, ---
+              case notes of ---
+                False -> "r" ---
+                True -> "") ---
+  write_octave :: Octave -> String ---
+  write_octave octave = ---
+    case compare octave 3 of ---
+      LT -> replicate (3 - octave) ',' ---
+      EQ -> "" ---
+      GT -> replicate (octave - 3) '\'' ---
+  write_option :: String -> String -> String ---
+  write_option option value = "#" <> write_round_brackets ("ly:set-option" <-> "'" <> option <-> value) ---
+  write_options :: String ---
+  write_options = intercalate " " [write_option "delete-intermediate-files" "#t", write_option "no-point-and-click" "#t"] ---
+  write_override :: String -> String -> String ---
+  write_override name value = "\\override" <-> write_eq name value ---
+  write_part :: Part -> Either Error String ---
+  write_part (Part {title, key, time, initial_position, stave_groups}) = ---
+    do ---
+      written_header <- write_header [Header_field' "piece" title] ---
+      written_set_key <- write_set_key key ---
+      written_time_and_initial_position <- write_time_and_initial_position ---
+      _ <- tracks_length (remove_notational_information stave_groups) ---
+      written_stave_groups <- traverse write_stave_group stave_groups ---
+      Right ---
+        ( ---
+          written_set_key <-> ---
+          written_time_and_initial_position <-> ---
+          "\\score" <-> ---
+          write_curly_brackets (written_header <-> write_angular_brackets_2 (intercalate " " written_stave_groups))) where ---
+    write_clef_and_stave :: Clef_and_stave note_type -> Either Error String ---
+    write_clef_and_stave (Clef_and_stave {clef, stave}) = ---
+      do ---
+        written_set_clef <- write_set_clef clef ---
+        written_tracks <- ---
+          case stave of ---
+            One_track events -> write_events (convert_events_to_pitched (Pitched_note 4 C) events) ---
+            Two_tracks events_0 events_1 -> ---
+              do ---
+                written_voice_0 <- write_voice "\\voiceOne" (convert_events_to_pitched (Pitched_note 4 D) events_0) ---
+                written_voice_1 <- write_voice "\\voiceTwo" (convert_events_to_pitched (Pitched_note 3 B) events_1) ---
+                Right (write_angular_brackets_2 (written_voice_0 <-> written_voice_1)) ---
+        Right ---
+          ( ---
+            "\\new" <-> ---
+            "Staff" <-> ---
+            "\\with" <-> ---
+            write_curly_brackets ---
+              (case clef of ---
+                Percussion_clef -> ---
+                  ( ---
+                    write_override "StaffSymbol.line-count" (show (length written_tracks)) <-> ---
+                    write_override "Stem.neutral-direction" "1") ---
+                Pitched_clef {} -> "") <-> ---
+            write_curly_brackets ---
+              ( ---
+                intercalate ---
+                  " " ---
+                  ( ---
+                    (case clef of ---
+                      Percussion_clef -> [] ---
+                      Pitched_clef {} -> ["\\Key"]) <> ---
+                    ["\\Time_and_initial_position", written_set_clef]) <-> ---
+                written_tracks <-> ---
+                write_end_bar_line)) ---
+    write_event :: Int -> Event Pitched -> StateT Composition.Lilypond.State (Either Error) (Bool, String) ---
+    write_event triplets event = ---
+      case event of ---
+        Event notes len -> ---
+          do ---
+            Composition.Lilypond.State {state_position} <- get ---
+            let position' = state_position + (2 % 3) ^ triplets * length_to_fraction len ---
+            position'' <- ---
+              case compare position' (measure_length time) of ---
+                LT -> return position' ---
+                EQ -> return 0 ---
+                GT -> throwError An_event_crosses_the_bar_line ---
+            #state_position .= position'' ---
+            (tie, written_notes) <- write_notes notes ---
+            return (tie, written_notes <> write_length len) ---
+        Triplet events -> ---
+          do ---
+            maybe_written_events <- write_events' (1 + triplets) events ---
+            case maybe_written_events of ---
+              Nothing -> throwError Empty_triplet ---
+              Just (tie, written_events) -> return (tie, "\\tuplet" <-> "3/2" <-> write_curly_brackets written_events) ---
+    write_events :: [Event Pitched] -> Either Error String ---
+    write_events events = ---
+      do ---
+        maybe_written_events <- ---
+          evalStateT ---
+            (write_events' 0 events) ---
+            (Composition.Lilypond.State {state_notes = False, state_position = initial_position_to_fraction initial_position}) ---
+        Right ---
+          (case maybe_written_events of ---
+            Nothing -> "" ---
+            Just (_, written_events) -> written_events) ---
+    write_events' :: Int -> [Event Pitched] -> StateT Composition.Lilypond.State (Either Error) (Maybe (Bool, String)) ---
+    write_events' triplets events = ---
+      case events of ---
+        [] -> return Nothing ---
+        event : events' -> ---
+          do ---
+            (tie_0, written_event) <- write_event triplets event ---
+            maybe_written_events <- write_events' triplets events' ---
+            return ---
+              (Just ---
+                ( ---
+                  tie_0, ---
+                  intercalate ---
+                    " " ---
+                    ( ---
+                      written_event : ---
+                      case maybe_written_events of ---
+                        Nothing -> [] ---
+                        Just (tie_1, written_events) -> ---
+                          ( ---
+                            (case tie_1 of ---
+                              False -> [] ---
+                              True -> ["~"]) <> ---
+                            [written_events])))) ---
+    write_initial_position :: Either Error String ---
+    write_initial_position = ---
+      do ---
+        let initial_position' = initial_position_to_fraction initial_position ---
+        check
+          (Is_out_of_range_without_location Initial_position_IOOR)
+          (between 0 (measure_length time - 1 % 128) initial_position') ---
+        let num :% den = measure_length time - initial_position' ---
+        Right (show den <-> "*" <-> show num) ---
+    write_instrument_clefs_and_staves :: Pitched_or_unpitched Instrument_clefs_and_staves -> Either Error String ---
+    write_instrument_clefs_and_staves = pitched_and_unpitched write_instrument_clefs_and_staves' ---
+    write_instrument_clefs_and_staves' :: Instrument_clefs_and_staves note_type -> Either Error String ---
+    write_instrument_clefs_and_staves' (Instrument_clefs_and_staves {instrument_name, clefs_and_staves}) = ---
+      do ---
+        written_instrument_name <- write_quotes instrument_name ---
+        written_clefs_and_staves <- traverse write_clef_and_stave clefs_and_staves ---
+        Right ---
+          ( ---
+            "\\new" <-> ---
+            "PianoStaff" <-> ---
+            "\\with" <-> ---
+            write_curly_brackets (write_eq "instrumentName" written_instrument_name) <-> ---
+            write_angular_brackets_2 (intercalate " " written_clefs_and_staves)) ---
+    write_set_initial_position :: Either Error String ---
+    write_set_initial_position = ---
+      do ---
+        written_initial_position <- write_initial_position ---
+        Right ("\\partial" <-> written_initial_position) ---
+    write_stave_group :: [Pitched_or_unpitched Instrument_clefs_and_staves] -> Either Error String ---
+    write_stave_group stave_group = ---
+      case stave_group of ---
+        [instrument_clefs_and_staves] -> write_instrument_clefs_and_staves instrument_clefs_and_staves ---
+        _ -> ---
+          do ---
+            written_instruments_clefs_and_staves <- traverse write_instrument_clefs_and_staves stave_group ---
+            Right ("\\new" <-> "StaffGroup" <-> write_angular_brackets_2 (intercalate " " written_instruments_clefs_and_staves)) ---
+    write_time_and_initial_position :: Either Error String ---
+    write_time_and_initial_position = ---
+      do ---
+        written_set_initial_position <- write_set_initial_position ---
+        Right ( ---
+          write_eq ---
+            "Time_and_initial_position" ---
+            (write_curly_brackets ---
+              (intercalate " " (["\\numericTimeSignature", write_set_time time, written_set_initial_position])))) ---
+    write_voice :: String -> [Event Pitched] -> Either Error String ---
+    write_voice voice_name events = ---
+      do ---
+        written_events <- write_events events ---
+        Right ("\\new" <-> "Voice" <-> write_curly_brackets (voice_name <-> write_curly_brackets written_events)) ---
+  write_quotes :: String -> Either Error String ---
+  write_quotes text = ---
+    do ---
+      traverse_ check_char text ---
+      Right (write_brackets "\"" "\"" text) ---
+  write_round_brackets :: String -> String ---
+  write_round_brackets = write_brackets "(" ")" ---
+  write_score :: Score -> Either Error String ---
+  write_score (Score {title, header, parts}) = ---
+    do ---
+      written_parts <- traverse write_part parts ---
+      written_language <- write_language ---
+      written_header <- ---
+        write_header (delete_default_tagline : Header_field' "title" title : (convert_header_field <$> assocs header)) ---
+      Right (write_options <-> written_language <-> written_header <-> intercalate " " written_parts) ---
+  write_set_clef :: Clef note_type -> Either Error String ---
+  write_set_clef clef = ---
+    do ---
+      written_clef <- write_quotes (write_clef clef) ---
+      Right ("\\clef" <-> written_clef) ---
+  write_set_key :: Key -> Either Error String ---
+  write_set_key key = ---
+    do ---
+      written_key <- write_key key ---
+      Right (write_eq "Key" (write_curly_brackets ("\\key" <-> written_key))) ---
+  write_set_time :: Time -> String ---
+  write_set_time time = "\\time" <-> write_time time ---
+  write_time :: Time -> String ---
+  write_time (Time num den) = write_time_numerator num <> "/" <> write_basic_length den ---
+  write_time_numerator :: [Time_numerator_factor] -> String ---
+  write_time_numerator num = show (time_numerator_to_int num) ---
diff --git a/Composition/MIDI.hs b/Composition/MIDI.hs
new file mode 100644
--- /dev/null
+++ b/Composition/MIDI.hs
@@ -0,0 +1,203 @@
+{-|
+Description: Generating MIDI files.
+-}
+module Composition.MIDI (midi) where
+  import Composition.Errors
+  import Composition.Notes
+  import Composition.Score
+  import Composition.Theory
+  import Composition.Time
+  import Control.Monad
+  import Control.Monad.Except
+  import Control.Monad.State.Strict
+  import Control.Monad.Trans.Except
+  import Data.ByteString as ByteString
+  import Data.Foldable as Foldable
+  import Data.List as List
+  import Data.Ratio
+  import Data.Set as Set
+  import Data.Word
+  import Parser.Files
+  import Parser.Utilities
+  data MIDI_part = MIDI_part {time_denominator :: Basic_length, tempo :: Tempo, tracks :: [Pitched_or_unpitched Track]}
+  deriving instance Show MIDI_part
+  encode_chunk :: [Word8] -> [Word8] -> Either Error [Word8]
+  encode_chunk typ dat =
+    do
+      len <- encode_int_fixed 4 Track_length_IOOR (Foldable.length dat)
+      Right (typ <> len <> dat)
+  encode_format :: [Word8]
+  encode_format = [0, 1]
+  encode_int_fixed :: (MonadError Error f) => Integer -> Is_out_of_range_type -> Int -> f [Word8]
+  encode_int_fixed bytes typ i =
+    case bytes of
+      0 ->
+        case i of
+          0 -> return []
+          _ -> throwError (Is_out_of_range_without_location typ)
+      _ ->
+        do
+          let (i', last_byte) = divMod i 256
+          encoded_i' <- encode_int_fixed (bytes - 1) typ i'
+          return (encoded_i' <> [fromIntegral last_byte])
+  encode_int_flexible :: (MonadError Error f) => Int -> f [Word8]
+  encode_int_flexible = encode_int_flexible' 4 0
+  encode_int_flexible' :: (MonadError Error f) => Integer -> Word8 -> Int -> f [Word8]
+  encode_int_flexible' max_bytes msb i =
+    case max_bytes of
+      0 -> throwError (Is_out_of_range_without_location Event_or_part_length_IOOR)
+      _ ->
+        do
+          let (i', last_byte) = divMod i 128
+          encoded_i' <-
+            case i' of
+              0 -> return []
+              _ -> encode_int_flexible' (max_bytes - 1) 128 i'
+          return (encoded_i' <> [msb + fromIntegral last_byte])
+  encode_score :: [MIDI_part] -> Either Error [Word8]
+  encode_score parts =
+    do
+      header <- encode_header
+      encoded_parts <- traverse encode_part parts
+      tracks <- traverse encode_complete_track (List.transpose encoded_parts)
+      Right (header <> join tracks) where
+    encode_complete_track :: [[Word8]] -> Either Error [Word8]
+    encode_complete_track track_parts =
+      do
+        end_track <- encode_end_track
+        encode_chunk [77, 84, 114, 107] (join track_parts <> end_track)
+    encode_end_track :: Either Error [Word8]
+    encode_end_track = encode_metaevent 0 47 []
+    encode_events :: forall note_type .
+      MIDI_instrument -> Word8 -> Velocity -> [Event_fraction note_type] -> StateT [Word8] (Either Error) [Word8]
+    encode_events instrument channel velocity events =
+      do
+        check (Is_out_of_range_without_location Velocity_IOOR) (between 0 max_velocity velocity)
+        check (Is_out_of_range_without_location MIDI_instrument_code_IOOR) (between 0 127 instrument)
+        join <$> traverse encode_event (events <> [Event' Set.empty rest_after_part]) where
+      encode_event :: Event_fraction note_type -> StateT [Word8] (Either Error) [Word8]
+      encode_event (Event' notes len) =
+        do
+          encoded_notes <- traverse encode_note (elems notes)
+          notes_on <- traverse encode_note_on encoded_notes
+          rest <- encode_rest
+          notes_off <- traverse encode_note_off encoded_notes
+          return (join notes_on <> rest <> join notes_off) where
+        encode_rest :: StateT [Word8] (Either Error) [Word8]
+        encode_rest = encode_metaevent len 1 []
+      encode_note :: Note note_type -> StateT [Word8] (Either Error) Word8
+      encode_note note =
+        case note of
+          Pitched_note _ _ ->
+            do
+              check (Is_out_of_range_without_location Note_IOOR) (between min_note max_note note)
+              return (fromIntegral (distance_in_semitones min_note note))
+          Unpitched_note -> return instrument
+      encode_note_event :: Word8 -> Word8 -> StateT [Word8] (Either Error) [Word8]
+      encode_note_event typ note = encode_midi_event channel typ [note, velocity]
+      encode_note_off :: Word8 -> StateT [Word8] (Either Error) [Word8]
+      encode_note_off = encode_note_event 8
+      encode_note_on :: Word8 -> StateT [Word8] (Either Error) [Word8]
+      encode_note_on = encode_note_event 9
+    encode_metaevent :: (MonadError Error f) => Ratio Int -> Word8 -> [Word8] -> f [Word8]
+    encode_metaevent time typ dat = encode_midi_or_metaevent time ([255, typ, fromIntegral (Foldable.length dat)] <> dat)
+    encode_header :: Either Error [Word8]
+    encode_header =
+      do
+        encoded_number_of_tracks <- encode_int_fixed 2 The_number_of_tracks_IOOR number_of_tracks
+        length_of_quarter_note_in_ticks <-
+          encode_int_fixed 2 The_least_common_denominator_of_note_lengths_IOOR (length_in_ticks (1 % 4))
+        encode_chunk [77, 84, 104, 100] (encode_format <> encoded_number_of_tracks <> length_of_quarter_note_in_ticks)
+    encode_midi_event :: Word8 -> Word8 -> [Word8] -> StateT [Word8] (Either Error) [Word8]
+    encode_midi_event channel typ dat = encode_midi_or_metaevent 0 ([channel + 16 * typ] <> dat)
+    encode_midi_or_metaevent :: (MonadError Error f) => Ratio Int -> [Word8] -> f [Word8]
+    encode_midi_or_metaevent time event =
+      do
+        encoded_time <- encode_length
+        return (encoded_time <> event) where
+      encode_length :: (MonadError Error f) => f [Word8]
+      encode_length = encode_int_flexible (length_in_ticks time)
+    encode_part :: MIDI_part -> Either Error [[Word8]]
+    encode_part (MIDI_part {time_denominator, tempo, tracks}) =
+      do
+        encoded_tempo <- encode_tempo
+        empty_track <- create_empty_track
+        encoded_tracks <- encode_tracks (tracks <> List.replicate (number_of_tracks - Foldable.length tracks) empty_track)
+        Right
+          (case encoded_tracks of
+            [] -> []
+            track : tracks' -> (encoded_tempo <> track) : tracks') where
+      create_empty_track :: Either Error (Pitched_or_unpitched Track)
+      create_empty_track =
+        do
+          len <- tracks_length tracks
+          Right (Unpitched (Track {instrument = 0, velocity = 0, events = [Event' Set.empty len]}))
+      encode_tempo :: Either Error [Word8]
+      encode_tempo =
+        do
+          encoded_tempo <- encode_int_fixed 3 Tempo_IOOR quarter_note_length_in_microseconds
+          encode_metaevent 0 81 encoded_tempo
+      quarter_note_length_in_microseconds :: Int
+      quarter_note_length_in_microseconds =
+        round (60000000 * denominator (basic_length_to_fraction time_denominator) % (4 * tempo))
+    encode_pitched_track :: Track Pitched -> StateT [Word8] (Either Error) [Word8]
+    encode_pitched_track (Track {instrument, velocity, events}) =
+      do
+        channel <- new_channel
+        encoded_instrument <- encode_instrument channel
+        encoded_events <- encode_events instrument channel velocity events
+        return (encoded_instrument <> encoded_events) where
+      encode_instrument :: Word8 -> StateT [Word8] (Either Error) [Word8]
+      encode_instrument channel = encode_midi_event channel 12 [instrument]
+    encode_track :: Pitched_or_unpitched Track -> StateT [Word8] (Either Error) [Word8]
+    encode_track = pitched_or_unpitched encode_pitched_track encode_unpitched_track
+    encode_tracks :: [Pitched_or_unpitched Track] -> Either Error [[Word8]]
+    encode_tracks tracks = evalStateT (traverse encode_track tracks) pitched_channels
+    encode_unpitched_track :: Track Unpitched -> StateT [Word8] (Either Error) [Word8]
+    encode_unpitched_track (Track {instrument, velocity, events}) = encode_events instrument unpitched_channel velocity events
+    lcd :: Int
+    lcd = 4 `lcm` lcm_all (lcd_part <$> parts)
+    length_in_ticks :: Length_fraction -> Int
+    length_in_ticks len = numerator (fromIntegral lcd * len)
+    number_of_tracks :: Int
+    number_of_tracks = Foldable.maximum (0 : (number_of_tracks_in_part <$> parts))
+  lcd_event :: Event_fraction note_type -> Int
+  lcd_event (Event' _ len) = denominator len
+  lcd_part :: MIDI_part -> Int
+  lcd_part (MIDI_part {time_denominator, tracks}) =
+    denominator (basic_length_to_fraction time_denominator) `lcm` lcm_all (pitched_and_unpitched lcd_track <$> tracks)
+  lcd_track :: Track note_type -> Int
+  lcd_track (Track {events}) = lcm_all (lcd_event <$> events)
+  max_note :: Note Pitched
+  max_note = Pitched_note 9 G
+  -- | Encode the score in MIDI format and write it to the specified file.
+  midi :: Score -> File_path -> ExceptT Error IO ()
+  midi score file_path =
+    do
+      encoded_score <- except (encode_score (midi_score score))
+      write_file "mid" ByteString.writeFile File_error file_path (pack encoded_score) where
+  midi_score :: Score -> [MIDI_part]
+  midi_score (Score {parts}) = midi_part <$> parts
+  midi_part :: Part -> MIDI_part
+  midi_part (Part {time = Time _ time_denominator, tempo, stave_groups}) =
+    MIDI_part {time_denominator, tempo, tracks = remove_notational_information stave_groups}
+  min_note :: Note Pitched
+  min_note = Pitched_note -1 C
+  new_channel :: StateT [Word8] (Either Error) Word8
+  new_channel =
+    do
+      channels <- get
+      case channels of
+        [] -> throwError (Is_out_of_range_without_location The_number_of_pitched_tracks_IOOR)
+        channel : channels' ->
+          do
+            put channels'
+            return channel
+  number_of_tracks_in_part :: MIDI_part -> Int
+  number_of_tracks_in_part (MIDI_part {tracks}) = Foldable.length tracks
+  pitched_channels :: [Word8]
+  pitched_channels = List.delete unpitched_channel [0 .. 15]
+  rest_after_part :: Length_fraction
+  rest_after_part = 1
+  unpitched_channel :: Word8
+  unpitched_channel = 9
diff --git a/Composition/Midi.hs b/Composition/Midi.hs
deleted file mode 100644
--- a/Composition/Midi.hs
+++ /dev/null
@@ -1,248 +0,0 @@
---------------------------------------------------------------------------------------------------------------------------------
-{-# OPTIONS_GHC -Wall #-}
-{-# LANGUAGE NegativeLiterals, StandaloneDeriving #-}
-{-|
-Description: A module for generating MIDI files.
-
-This module contains the data structures and the function for generating MIDI files from note sequences.
--}
-module Composition.Midi (Instrument (..), Track (..), Part (..), midi) where
-  import Composition.Notes (
-    Note (..),
-    Note_name (..),
-    Rat,
-    Simultaneous (..),
-    Time (..),
-    Time_and_position (..),
-    sequential_length)
-  import Composition.Theory (semitones_from_c)
-  import Control.Monad (join, zipWithM)
-  import Data.ByteString (pack, writeFile)
-  import Data.Foldable (traverse_)
-  import Data.List (delete, transpose)
-  import Data.Ratio ((%), numerator, denominator)
-  import Data.Word (Word8)
-  type Err = Either String
-  -- | MIDI instruments.
-  data Instrument =
-    Accordion |
-    Bassoon |
-    Bells |
-    Cello |
-    Clarinet |
-    Double_bass |
-    Dulcimer |
-    English_horn |
-    Flute |
-    French_horn |
-    Glockenspiel |
-    Guitar |
-    Harp |
-    Harpsichord |
-    Oboe |
-    Organ |
-    Piano |
-    Piccolo |
-    Pizzicato_strings |
-    Recorder |
-    Timpani |
-    Trombone |
-    Trumpet |
-    Viola |
-    Violin |
-    Voice
-  -- | Each part can have a different time signature, tempo and instrumentation. The second argument is the tempo specified in
-  -- beats per minute.
-  data Part = Part Time_and_position Int [Track]
-  -- | A track consists of one homorhythmic note sequence. Heterorhythmic voices require separate tracks.
-  data Track = Track Instrument [Simultaneous]
-  deriving instance Show Instrument
-  deriving instance Show Part
-  deriving instance Show Track
-  channels :: [Word8]
-  channels = delete 9 [0 .. 15]
-  check :: String -> Bool -> Err ()
-  check err condition =
-    case condition of
-      False -> Left err
-      True -> Right ()
-  check_range :: Ord t => String -> t -> t -> t -> Err ()
-  check_range typ min_t max_t x = check (typ ++ " out of range.") (min_t <= x && max_t >= x)
-  encode_chunk :: [Word8] -> [Word8] -> [Word8]
-  encode_chunk typ dat = typ ++ encode_int_fixed 4 (length dat) ++ dat
-  encode_end_track :: [Word8]
-  encode_end_track = encode_meta_event_0 47 []
-  encode_event :: Int -> Rat -> [Word8] -> [Word8]
-  encode_event lcd time dat = encode_time lcd time ++ dat
-  encode_format :: [Word8]
-  encode_format = [0, 1]
-  encode_header :: Int -> Int -> Err [Word8]
-  encode_header number_of_tracks lcd =
-    do
-      let quarter = length_in_ticks lcd (1 % 4)
-      check_range "The number of ticks in quarter note" 1 max_ticks_in_quarter_note quarter
-      Right
-        (encode_chunk
-          [77, 84, 104, 100]
-          (encode_format ++ encode_int_fixed 2 (1 + number_of_tracks) ++ encode_int_fixed 2 quarter))
-  encode_instrument :: Word8 -> Instrument -> [Word8]
-  encode_instrument channel instrument = encode_midi_event channel 12 [encode_instrument' instrument]
-  encode_instrument' :: Instrument -> Word8
-  encode_instrument' instrument =
-    case instrument of
-      Accordion -> 21
-      Bassoon -> 70
-      Bells -> 14
-      Cello -> 42
-      Clarinet -> 71
-      Double_bass -> 43
-      Dulcimer -> 15
-      English_horn -> 69
-      Flute -> 73
-      French_horn -> 60
-      Glockenspiel -> 9
-      Guitar -> 24
-      Harp -> 46
-      Harpsichord -> 6
-      Oboe -> 68
-      Organ -> 19
-      Piano -> 0
-      Piccolo -> 72
-      Pizzicato_strings -> 45
-      Recorder -> 74
-      Timpani -> 47
-      Trombone -> 57
-      Trumpet -> 56
-      Viola -> 41
-      Violin -> 40
-      Voice -> 52
-  encode_int_fixed :: Integer -> Int -> [Word8]
-  encode_int_fixed n i =
-    case n of
-      0 -> []
-      _ -> encode_int_fixed (n - 1) (div i 256) ++ [fromIntegral i]
-  encode_int_flexible :: Int -> [Word8]
-  encode_int_flexible i = encode_int_flexible' (div i 128) ++ [fromIntegral (mod i 128)]
-  encode_int_flexible' :: Int -> [Word8]
-  encode_int_flexible' i =
-    case i of
-      0 -> []
-      _ -> encode_int_flexible' (div i 128) ++ [128 + fromIntegral (mod i 128)]
-  max_number_of_tracks :: Int
-  max_number_of_tracks = length channels
-  max_ticks_in_quarter_note :: Int
-  max_ticks_in_quarter_note = 2 ^ (16 :: Integer) - 1
-  max_track_length :: Int
-  max_track_length = 2 ^ (32 :: Integer) - 1
-  -- | Encodes the score in MIDI format and writes it to the specified file.
-  midi :: String -> [Part] -> IO ()
-  midi file_name score =
-    case encode_score score of
-      Left err -> putStrLn ("Midi error. " ++ err)
-      Right encoding -> Data.ByteString.writeFile (file_name ++ ".mid") (pack encoding)
-  encode_meta_event :: Int -> Rat -> Word8 -> [Word8] -> [Word8]
-  encode_meta_event lcd time typ dat = encode_event lcd time ([255, typ, fromIntegral (length dat)] ++ dat)
-  encode_meta_event_0 :: Word8 -> [Word8] -> [Word8]
-  encode_meta_event_0 = encode_meta_event 1 0
-  encode_midi_event :: Word8 -> Word8 -> [Word8] -> [Word8]
-  encode_midi_event channel typ dat = encode_event 1 0 ([channel + 16 * typ] ++ dat)
-  encode_note :: Note -> Word8
-  encode_note (Note octave note_name) = fromIntegral (12 * (1 + octave) + semitones_from_c note_name)
-  encode_note_off :: Word8 -> Note -> [Word8]
-  encode_note_off channel note = encode_midi_event channel 8 [encode_note note, velocity]
-  encode_note_on :: Word8 -> Note -> [Word8]
-  encode_note_on channel note = encode_midi_event channel 9 [encode_note note, velocity]
-  encode_part :: Int -> Int -> Part -> Err [[Word8]]
-  encode_part number_of_tracks lcd (Part (Time_and_position time _) tempo tracks) =
-    do
-      tempo' <- encode_tempo time tempo
-      let tracks' = (\(Track instrument sequential) -> Track instrument (sequential ++ [Simultaneous [] 1])) <$> tracks
-      len <-
-        case track_length <$> tracks' of
-          [] -> Right 1
-          len' : lengths ->
-            do
-              check "Track length mismatch." (all ((==) len') lengths)
-              Right len'
-      check_range "Part length" 0 max_note_length (length_in_ticks lcd len)
-      let rest = encode_rest lcd len
-      tracks'' <- zipWithM (encode_track lcd) channels tracks'
-      Right ([tempo' ++ rest] ++ tracks'' ++ replicate (number_of_tracks - length tracks') rest)
-  encode_rest :: Int -> Rat -> [Word8]
-  encode_rest lcd len = encode_text lcd len []
-  encode_score :: [Part] -> Err [Word8]
-  encode_score parts =
-    do
-      let parts' = parts ++ [Part (Time_and_position (Time [2, 2] 4) 0) 100 []]
-      let number_of_tracks = maximum (number_of_tracks_in_part <$> parts')
-      check_range "Number of tracks" 0 max_number_of_tracks number_of_tracks
-      let lcd = lcm_all (lcd_of_part <$> parts')
-      header <- encode_header number_of_tracks lcd
-      parts'' <- traverse (encode_part number_of_tracks lcd) parts'
-      tracks <- traverse encode_tracks (transpose parts'')
-      Right (header ++ join tracks)
-  encode_sequential :: Int -> Word8 -> [Simultaneous] -> Err [Word8]
-  encode_sequential lcd channel sequential = join <$> traverse (encode_simultaneous lcd channel) sequential
-  encode_simultaneous :: Int -> Word8 -> Simultaneous -> Err [Word8]
-  encode_simultaneous lcd channel (Simultaneous notes len) =
-    do
-      traverse_ (check_range "Note" min_note max_note) notes
-      check "Non-positive note length." (0 < len)
-      Right ((notes >>= encode_note_on channel) ++ encode_rest lcd len ++ (notes >>= encode_note_off channel))
-  encode_tempo :: Time -> Int -> Err [Word8]
-  encode_tempo time tempo =
-    do
-      let tempo' = microseconds_in_quarter_note time tempo
-      check_range "Tempo" 1 max_tempo tempo'
-      Right (encode_meta_event_0 81 (encode_int_fixed 3 tempo'))
-  encode_text :: Int -> Rat -> [Word8] -> [Word8]
-  encode_text lcd time = encode_meta_event lcd time 1
-  encode_time :: Int -> Rat -> [Word8]
-  encode_time lcd time = encode_int_flexible (length_in_ticks lcd time)
-  encode_track :: Int -> Word8 -> Track -> Err [Word8]
-  encode_track lcd channel (Track instrument sequential) =
-    do
-      sequential' <- encode_sequential lcd channel sequential
-      Right (encode_instrument channel instrument ++ sequential')
-  encode_tracks :: [[Word8]] -> Err [Word8]
-  encode_tracks tracks =
-    do
-      let track = join tracks ++ encode_end_track
-      check_range "Track length" 0 max_track_length (length track)
-      Right (encode_chunk [77, 84, 114, 107] track)
-  lcd_of_part :: Part -> Int
-  lcd_of_part (Part time_and_initial_position _ tracks) =
-    lcm (lcd_of_time_and_position time_and_initial_position) (lcm_all (lcd_of_track <$> tracks))
-  lcd_of_sequential :: [Simultaneous] -> Int
-  lcd_of_sequential sequential = lcm_all (lcd_of_simultaneous <$> sequential)
-  lcd_of_simultaneous :: Simultaneous -> Int
-  lcd_of_simultaneous (Simultaneous _ len) = denominator len
-  lcd_of_time_and_position :: Time_and_position -> Int
-  lcd_of_time_and_position (Time_and_position time position) = lcm (time_denominator time) (denominator position)
-  lcd_of_track :: Track -> Int
-  lcd_of_track (Track _ sequential) = lcd_of_sequential sequential
-  lcm_all :: Integral t => [t] -> t
-  lcm_all = foldr lcm 1
-  length_in_ticks :: Int -> Rat -> Int
-  length_in_ticks lcd time = numerator time * div lcd (denominator time)
-  max_note :: Note
-  max_note = Note 9 G
-  max_note_length :: Int
-  max_note_length = 2 ^ (32 :: Integer) - 1
-  max_tempo :: Int
-  max_tempo = 2 ^ (24 :: Integer) - 1
-  microseconds_in_minute :: Int
-  microseconds_in_minute = 60000000
-  microseconds_in_quarter_note :: Time -> Int -> Int
-  microseconds_in_quarter_note (Time _ den) tempo = round (microseconds_in_minute * den % (4 * tempo))
-  min_note :: Note
-  min_note = Note -2 B_sharp
-  number_of_tracks_in_part :: Part -> Int
-  number_of_tracks_in_part (Part _ _ tracks) = length tracks
-  time_denominator :: Time -> Int
-  time_denominator (Time _ den) = den
-  track_length :: Track -> Rat
-  track_length (Track _ sequential) = sequential_length sequential
-  velocity :: Word8
-  velocity = 127
---------------------------------------------------------------------------------------------------------------------------------
diff --git a/Composition/Notes.hs b/Composition/Notes.hs
--- a/Composition/Notes.hs
+++ b/Composition/Notes.hs
@@ -1,458 +1,195 @@
---------------------------------------------------------------------------------------------------------------------------------
-{-# OPTIONS_GHC -Wall #-}
-{-# LANGUAGE DeriveLift, StandaloneDeriving, TemplateHaskell #-}
 {-|
-Description: A module for basic musical data structures.
-
-This module contains some data structures for describing note sequences and time signatures, and a QuasiQuoter for note
-sequences.
+Basic musical data structures: notes, lengths and events.
 -}
 module Composition.Notes (
+  Note_type (..),
   Accidental (..),
+  Basic_length (..),
+  Branch (..),
+  Dot (..),
+  Event (..),
+  Event' (..),
+  Event_fraction,
+  Length (..),
+  Length_fraction,
   Natural_note_name (..),
   Note (..),
   Note_name (..),
-  Note_name' (..),
-  Rat,
-  Simultaneous (..),
-  Time (..),
-  Time_and_position (..),
+  Notes (..),
+  Octave,
+  Pitched_or_unpitched (..),
+  accidentals,
+  basic_length_denominator,
+  basic_length_to_fraction,
   construct_note_name,
   deconstruct_note_name,
-  ly,
-  measure_length,
-  sequential_length,
-  simultaneous_length,
-  subdivision) where
-  import Control.Monad.Except (MonadError (..))
-  import Control.Monad.RWS.Strict (RWS, RWST (..), runRWS)
-  import Control.Monad.State.Strict (MonadState (..), modify)
-  import Control.Monad.Writer.Strict (MonadWriter (..))
-  import Data.Char (isDigit)
-  import Data.Maybe (fromJust)
-  import Data.Ratio ((%), Ratio)
-  import Data.Tuple (swap)
-  import Language.Haskell.TH (Exp, Q)
-  import Language.Haskell.TH.Quote (QuasiQuoter (..))
-  import Language.Haskell.TH.Syntax (Lift (..))
-  -- | Accidentals.
-  data Accidental = Flat | Natural | Sharp
-  data Char' = Delimiter_char Token | Nat_char Char | Negation_char | Space_char
-  data Counter = Counter Integer
-  -- | Natural note names for algorithmic convenience.
-  data Natural_note_name = C_natural | D_natural | E_natural | F_natural | G_natural | A_natural | B_natural
-  -- | Note. The first argument is the octave.
-  data Note = Note Int Note_name
-  -- | Note names. Double accidentals are not supported.
-  data Note_name =
-    C_flat |
-    C |
-    C_sharp |
-    D_flat |
-    D |
-    D_sharp |
-    E_flat |
-    E |
-    F_flat |
-    E_sharp |
-    F |
-    F_sharp |
-    G_flat |
-    G |
-    G_sharp |
-    A_flat |
-    A |
-    A_sharp |
-    B_flat |
-    B |
-    B_sharp
-  -- | An alternative representation of note names that is more convenient for algorithms.
-  data Note_name' =
-    Note_name' Natural_note_name Accidental
-  type Parser = WST Counter [Token] Maybe
-  -- | Rationals with limited numerators and denominators for representing note lengths.
-  type Rat = Ratio Int
-  -- | A collection of notes of same length that sound simultaneously. Non-positive length will result in runtime errors when
-  -- attempting to generate Lilypond and MIDI files.
-  data Simultaneous = Simultaneous [Note] Rat
-  -- | Time signature. The first argument describes the subdivisions of the bar. For example, 2 beats per bar is encoded as [2],
-  -- 3 as [3], 4 as [2, 2], 6 as [2, 3], 9 as [3, 3]. The second argument is the inverse of the length of the beat. Note that
-  -- non-positive numbers in either numerator or denominator will result in errors, and Lilypond does not accept denominators
-  -- that are not a power of two.
-  data Time = Time [Int] Int
-  -- | Time signature and the starting position of the first bar. For example, if the piece starts with a full bar, the initial
-  -- position is 0. If the piece is in 3/4 and starts with a 1/4-note bar, the initial position is 1/2.
-  data Time_and_position = Time_and_position Time Rat
-  data Token =
-    Dot_token |
-    End_token |
-    Flat_token |
-    Left_angular_token |
-    Nat_token Int |
-    Negation_token |
-    Note_name_token Natural_note_name |
-    Right_angular_token |
-    Sharp_token |
-    Tie_token
-  type Tokeniser = WS [Token] [Char']
-  type WS output state = RWS () output state
-  type WST = RWST ()
-  infixl 3 <+>
-  (<+>) :: Parser t -> Parser t -> Parser t
-  parse_0 <+> parse_1 =
-    wst
-      (\tokens ->
-        case (runWST parse_0 tokens, runWST parse_1 tokens) of
-          (Nothing, Nothing) -> Nothing
-          (Nothing, Just result) -> Just result
-          (Just result, Nothing) -> Just result
-          (Just result_0, Just result_1) ->
-            case compare (get_token_counter result_0) (get_token_counter result_1) of
-              LT -> Just result_1
-              EQ -> Nothing
-              GT -> Just result_0)
-  deriving instance Enum Natural_note_name
-  instance Enum Note where
-    fromEnum (Note octave note_name) =
-      (
-        21 * octave +
-        fromEnum note_name -
-        case note_name of
-          C_flat -> 2
-          B_sharp -> 0
-          _ -> 1)
-    toEnum i =
-      let
-        octave = div i 21
-      in
-        case mod i 21 of
-          19 -> Note (1 + octave) C_flat
-          20 -> Note octave B_sharp
-          j -> Note octave (toEnum (1 + j))
-  deriving instance Enum Note_name
-  deriving instance Eq Accidental
-  deriving instance Eq Counter
-  deriving instance Eq Natural_note_name
-  deriving instance Eq Note
-  deriving instance Eq Note_name
-  deriving instance Eq Note_name'
-  deriving instance Eq Token
-  deriving instance Lift Note
-  deriving instance Lift Note_name
-  deriving instance Lift Simultaneous
-  instance Monoid Counter where
-    mempty = 0
-  instance Num Counter where
-    Counter i * Counter j = Counter (i * j)
-    Counter i + Counter j = Counter (i + j)
-    abs (Counter i) = Counter (abs i)
-    fromInteger i = Counter i
-    negate (Counter i) = Counter (negate i)
-    signum (Counter i) = Counter (signum i)
-  deriving instance Ord Counter
-  instance Ord Note where
-    compare (Note octave_0 note_name_0) (Note octave_1 note_name_1) =
-      case compare octave_0 octave_1 of
-        LT ->
-          case (note_name_0, note_name_1) of
-            (B_sharp, C_flat) -> GT
-            _ -> LT
-        EQ -> compare note_name_0 note_name_1
-        GT ->
-          case (note_name_0, note_name_1) of
-            (C_flat, B_sharp) -> LT
-            _ -> GT
-  deriving instance Ord Note_name
-  instance Semigroup Counter where
-    (<>) = (+)
-  deriving instance Show Accidental
-  deriving instance Show Char'
-  deriving instance Show Counter
-  deriving instance Show Natural_note_name
-  deriving instance Show Note
-  deriving instance Show Note_name
-  deriving instance Show Note_name'
-  deriving instance Show Simultaneous
-  deriving instance Show Time
-  deriving instance Show Time_and_position
-  deriving instance Show Token
-  add_token :: Token -> Tokeniser ()
-  add_token token = tell [token]
-  certain_token :: Token -> Token -> Maybe ()
-  certain_token token token' =
-    case token == token' of
-      False -> Nothing
-      True -> Just ()
-  classify_char :: Char -> Char'
-  classify_char c =
-    case c of
-      ' ' -> Space_char
-      '#' -> Delimiter_char Sharp_token
-      '-' -> Negation_char
-      '.' -> Delimiter_char Dot_token
-      '<' -> Delimiter_char Left_angular_token
-      '>' -> Delimiter_char Right_angular_token
-      'A' -> Delimiter_char (Note_name_token A_natural)
-      'B' -> Delimiter_char (Note_name_token B_natural)
-      'C' -> Delimiter_char (Note_name_token C_natural)
-      'D' -> Delimiter_char (Note_name_token D_natural)
-      'E' -> Delimiter_char (Note_name_token E_natural)
-      'F' -> Delimiter_char (Note_name_token F_natural)
-      'G' -> Delimiter_char (Note_name_token G_natural)
-      'b' -> Delimiter_char Flat_token
-      '~' -> Delimiter_char Tie_token
-      _ ->
-        case isDigit c of
-          False -> error "Invalid character."
-          True -> Nat_char c
-  -- | Construct the note name from a natural note name and an accidental.
-  construct_note_name :: Note_name' -> Note_name
-  construct_note_name note_name = fromJust (lookup note_name (swap <$> note_names))
-  -- | Deconstruct a note name into the natural note name and the accidental.
-  deconstruct_note_name :: Note_name -> Note_name'
-  deconstruct_note_name note_name = fromJust (lookup note_name note_names)
-  gather_nat :: Tokeniser String
-  gather_nat =
-    do
-      maybe_char <- get_char 0
-      case maybe_char of
-        Just (Nat_char c) ->
-          do
-            next_char
-            i <- gather_nat
-            return (c : i)
-        _ -> return ""
-  get_char :: Integer -> Tokeniser (Maybe Char')
-  get_char i = index i <$> get
-  get_token_counter :: (t, [Token], Counter) -> Counter
-  get_token_counter (_, _, token_counter) = token_counter
-  index :: Integer -> [t] -> Maybe t
-  index i x =
+  denominator_to_basic_length,
+  events_length,
+  length_to_fraction,
+  lg,
+  next_basic_length,
+  pitched_and_unpitched,
+  pitched_or_unpitched,
+  scale_lengths) where
+  import Control.Lens.Combinators ---
+  import Data.Functor.Barbie ---
+  import Data.Generics.Labels () ---
+  import Data.Maybe ---
+  import Data.Ratio ---
+  import Data.Set
+  import Data.Tuple ---
+  import GHC.Generics ---
+  import Parser.Utilities ---
+  import Text.Read
+  -- | Notes can be either pitched or unpitched.
+  type data Note_type = Pitched | Unpitched
+  -- | Accidentals. Mostly for internal use. ---
+  data Accidental = Flat | Natural | Sharp ---
+  -- | Basic note lengths.
+  data Basic_length = Whole | Half | Quarter | Eighth | Sixteenth | Thirty_second | Sixty_fourth | One_hundred_and_twenty_eighth
+  -- | A term-level representation of the pitched / unpitched type for branching over the type. Mostly for internal use. ---
+  data Branch note_type where ---
+    Branch_pitched :: Branch Pitched ---
+    Branch_unpitched :: Branch Unpitched ---
+  -- | Dots. ---
+  data Dot = Dot ---
+  -- | Events. ---
+  data Event note_type = Event (Notes note_type) Length | Triplet [Event note_type] ---
+  -- | Generalised events with arbitrary notes type stripped of note length display information. Mostly for internal use.
+  data Event' notes = Event' {event_notes :: notes, event_length :: Length_fraction}
+  -- | Events stripped of note length display information. Mostly for internal use. ---
+  type Event_fraction note_type = Event' (Set (Note note_type)) ---
+  -- | Note lengths. ---
+  data Length = Length Basic_length [Dot] ---
+  -- | Note length as a fraction. Mostly for internal use. ---
+  type Length_fraction = Ratio Int ---
+  -- | Natural note names. Mostly for internal use. ---
+  data Natural_note_name = C_natural | D_natural | E_natural | F_natural | G_natural | A_natural | B_natural ---
+  -- | Pitched and unpitched notes. ---
+  data Note note_type where ---
+    Pitched_note :: Octave -> Note_name -> Note Pitched ---
+    Unpitched_note :: Note Unpitched ---
+  -- | Note names. ---
+  data Note_name = ---
+    C | C_sharp | D_flat | D | D_sharp | E_flat | E | F | F_sharp | G_flat | G | G_sharp | A_flat | A | A_sharp | B_flat | B ---
+  -- | Play a new set of notes or continue the previous set of notes. ---
+  data Notes note_type = Notes (Set (Note note_type)) | Tie ---
+  -- | Octaves in scientific pitch notation. ---
+  type Octave = Int ---
+  -- | This data type allows you to, for example, mix pitched and unpitched tracks in one list. ---
+  data Pitched_or_unpitched f = Pitched (f Pitched) | Unpitched (f Unpitched) ---
+  deriving instance Eq Accidental ---
+  deriving instance Bounded Accidental
+  deriving instance Bounded Natural_note_name ---
+  deriving instance Bounded Note_name ---
+  deriving instance Enum Accidental
+  deriving instance Enum Basic_length
+  deriving instance Enum Natural_note_name ---
+  instance Enum (Note Pitched) where ---
+    fromEnum (Pitched_note octave note_name) = 17 * octave + fromEnum note_name ---
+    toEnum i = Pitched_note (div i 17) (toEnum (mod i 17)) ---
+  deriving instance Enum Note_name ---
+  deriving instance Generic (Event' notes) ---
+  deriving instance Eq Basic_length ---
+  deriving instance Eq Natural_note_name ---
+  deriving instance Eq (Note note_type) ---
+  deriving instance Eq Note_name ---
+  instance FunctorB Pitched_or_unpitched where ---
+    bmap f = pitched_or_unpitched (Pitched <$> f) (Unpitched <$> f) ---
+  deriving instance Ord Accidental
+  deriving instance Ord Basic_length
+  deriving instance Ord Natural_note_name ---
+  deriving instance Ord (Note note_type) ---
+  deriving instance Ord Note_name ---
+  deriving instance Read Natural_note_name
+  deriving instance Read Note_name
+  deriving instance Show Accidental ---
+  deriving instance Show Basic_length ---
+  deriving instance Show (Branch note_type) ---
+  deriving instance Show Dot ---
+  deriving instance Show (Event note_type) ---
+  deriving instance (Show notes) => Show (Event' notes)
+  deriving instance Show Length ---
+  deriving instance Show Natural_note_name ---
+  deriving instance Show (Note note_type) ---
+  deriving instance Show Note_name ---
+  deriving instance Show (Notes note_type) ---
+  deriving instance (forall note_type . Show (f note_type)) => Show (Pitched_or_unpitched f) ---
+  instance TraversableB Pitched_or_unpitched where ---
+    btraverse f = pitched_or_unpitched ((<$>) Pitched <$> f) ((<$>) Unpitched <$> f) ---
+  -- | String representations of accidentals. Mostly for internal use.
+  accidentals :: [(Accidental, String)]
+  accidentals = [(Flat, "b"), (Natural, ""), (Sharp, "#")]
+  accidentals' :: [(Accidental, String)]
+  accidentals' = [(Flat, "_flat"), (Natural, ""), (Sharp, "_sharp")]
+  -- | Convert basic length to fraction denominator. Mostly for internal use. ---
+  basic_length_denominator :: Basic_length -> Int ---
+  basic_length_denominator len = 2 ^ basic_length_to_lg_denominator len ---
+  -- | Convert basic length to fraction. Mostly for internal use. ---
+  basic_length_to_fraction :: Basic_length -> Length_fraction ---
+  basic_length_to_fraction len = 1 % basic_length_denominator len ---
+  basic_length_to_lg_denominator :: Basic_length -> Int ---
+  basic_length_to_lg_denominator len = fromJust (lookup len basic_lengths) ---
+  basic_lengths :: [(Basic_length, Int)] ---
+  basic_lengths = ---
+    [ ---
+      (Whole, 0), ---
+      (Half, 1), ---
+      (Quarter, 2), ---
+      (Eighth, 3), ---
+      (Sixteenth, 4), ---
+      (Thirty_second, 5), ---
+      (Sixty_fourth, 6), ---
+      (One_hundred_and_twenty_eighth, 7)] ---
+  -- | Construct the note name from a natural note name and an accidental. Mostly for internal use.
+  construct_note_name :: Natural_note_name -> Accidental -> Maybe Note_name
+  construct_note_name natural_note_name accidental =
+    readMaybe (head (show natural_note_name) : fromJust (lookup accidental accidentals'))
+  -- | Deconstruct a note name into the natural note name and the accidental. Mostly for internal use.
+  deconstruct_note_name :: Note_name -> (Natural_note_name, Accidental)
+  deconstruct_note_name note_name =
+    case show note_name of
+      "" -> undefined
+      natural_note_name : accidental ->
+        (read (natural_note_name : "_natural"), fromJust (lookup accidental (swap <$> accidentals')))
+  -- | Convert fraction denominator to basic length. Mostly for internal use. ---
+  denominator_to_basic_length :: Int -> Maybe Basic_length ---
+  denominator_to_basic_length i = lg i >>= lg_denominator_to_basic_length ---
+  -- | Events length. ---
+  events_length :: [Event' notes] -> Length_fraction ---
+  events_length events = sum (view #event_length <$> events) ---
+  -- | Convert length to fraction. Mostly for internal use. ---
+  length_to_fraction :: Length -> Length_fraction ---
+  length_to_fraction (Length len dots) = basic_length_to_fraction len * (2 - 1 % (1 + length dots)) ---
+  -- | Binary logarithm. Mostly for internal use. ---
+  lg :: Int -> Maybe Int ---
+  lg i = ---
+    do ---
+      check () (i > 0) ---
+      lg' i ---
+  lg' :: Int -> Maybe Int ---
+  lg' i = ---
+    case i of ---
+      1 -> Just 0 ---
+      _ -> ---
+        case mod i 2 of ---
+          0 -> (+) 1 <$> lg' (div i 2) ---
+          1 -> Nothing ---
+          _ -> undefined ---
+  lg_denominator_to_basic_length :: Int -> Maybe Basic_length ---
+  lg_denominator_to_basic_length i = lookup i (swap <$> basic_lengths) ---
+  -- | Divide basic note length by two. ---
+  next_basic_length :: Basic_length -> Maybe Basic_length ---
+  next_basic_length len = lg_denominator_to_basic_length (1 + basic_length_to_lg_denominator len) ---
+  -- | Perform an operation that doesn't depend on whether the underlying structure contains pitched or unpitched notes. ---
+  pitched_and_unpitched :: (forall note_type . f note_type -> t) -> Pitched_or_unpitched f -> t ---
+  pitched_and_unpitched f = pitched_or_unpitched f f ---
+  -- | Perform an operation that behaves differently when the underlying structure contains pitched or unpitched notes.
+  pitched_or_unpitched :: (f Pitched -> t) -> (f Unpitched -> t) -> Pitched_or_unpitched f -> t
+  pitched_or_unpitched f_pitched f_unpitched x =
     case x of
-      [] -> Nothing
-      y : z ->
-        case i of
-          0 -> Just y
-          _ -> index (i - 1) z
-  invalid_template_location :: String -> Q t
-  invalid_template_location = template_error "Invalid location for template ly."
-  -- | A QuasiQuoter for compile-time parsing of note sequences. The syntax is loosely based on Lilypond. Some example of use:
-  --
-  -- * An empty note sequence: @[ly||]@.
-  -- * A single note: @[ly|\<C0>1|]@.
-  -- * Two consecutive notes: @[ly|\<C0>1 \<C0>1|]@.
-  -- * A rest, a single note and two simultaneous notes: @[ly|\<>1 \<C0>1 \<C0 D0>1|]@.
-  -- * All natural notes from C to B: @[ly|\<C0 D0 E0 F0 G0 A0 B0>1|]@.
-  -- * Accidentals: @[ly|\<Cb0 C0 C#0>1|]@.
-  -- * Different octaves: @[ly|\<C-1 C0 C1>1|]@.
-  -- * Rests of length 1\/3, 1\/2, 2\/3, 1, 3\/2, 2 and 3: @[ly|\<>3 \<>2 \<>3~3 \<>1 \<>1. \<>1~1 \<>1~1~1|]@.
-  ly :: QuasiQuoter
-  ly = QuasiQuoter ly_exp invalid_template_location invalid_template_location invalid_template_location
-  ly_exp :: String -> Q Exp
-  ly_exp = parse parse_sequential
-  -- | The length of one measure.
-  measure_length :: Time -> Rat
-  measure_length (Time numerator denominator) = product numerator % denominator
-  nat_token :: Token -> Maybe Int
-  nat_token token =
-    case token of
-      Nat_token i -> Just i
-      _ -> Nothing
-  next_char :: Tokeniser ()
-  next_char = modify tail
-  note_name_token :: Token -> Maybe Natural_note_name
-  note_name_token token =
-    case token of
-      Note_name_token natural_note_name -> Just natural_note_name
-      _ -> Nothing
-  note_names :: [(Note_name, Note_name')]
-  note_names =
-    [
-      (C_flat, Note_name' C_natural Flat),
-      (C, Note_name' C_natural Natural),
-      (C_sharp, Note_name' C_natural Sharp),
-      (D_flat, Note_name' D_natural Flat),
-      (D, Note_name' D_natural Natural),
-      (D_sharp, Note_name' D_natural Sharp),
-      (E_flat, Note_name' E_natural Flat),
-      (E, Note_name' E_natural Natural),
-      (E_sharp, Note_name' E_natural Sharp),
-      (F_flat, Note_name' F_natural Flat),
-      (F, Note_name' F_natural Natural),
-      (F_sharp, Note_name' F_natural Sharp),
-      (G_flat, Note_name' G_natural Flat),
-      (G, Note_name' G_natural Natural),
-      (G_sharp, Note_name' G_natural Sharp),
-      (A_flat, Note_name' A_natural Flat),
-      (A, Note_name' A_natural Natural),
-      (A_sharp, Note_name' A_natural Sharp),
-      (B_flat, Note_name' B_natural Flat),
-      (B, Note_name' B_natural Natural),
-      (B_sharp, Note_name' B_natural Sharp)]
-  parse :: Lift t => Parser t -> String -> Q Exp
-  parse parse_t text =
-    let
-      x = parse' parse_t (tokenise text)
-    in
-      [e|x|]
-  parse' :: Parser t -> [Token] -> t
-  parse' parse_t tokens =
-    case runWST (parse_end parse_t) tokens of
-      Nothing -> template_error "Parse error."
-      Just (x, _, _) -> x
-  parse_accidental :: Parser Accidental
-  parse_accidental = parse_flat <+> parse_natural <+> parse_sharp
-  parse_angular :: Parser t -> Parser t
-  parse_angular = parse_brackets Left_angular_token Right_angular_token
-  parse_base_length :: Parser Rat
-  parse_base_length = (%) 1 <$> parse_nat
-  parse_brackets :: Token -> Token -> Parser t -> Parser t
-  parse_brackets left_bracket right_bracket parse_t =
-    do
-      parse_token left_bracket
-      x <- parse_t
-      parse_token right_bracket
-      return x
-  parse_dots :: Parser Int
-  parse_dots = length <$> parse_many (parse_token Dot_token)
-  parse_element :: Token -> Parser t -> Parser t
-  parse_element separator parse_t =
-    do
-      parse_token separator
-      parse_t
-  parse_empty :: Parser [t]
-  parse_empty = return []
-  parse_end :: Parser t -> Parser t
-  parse_end parse_t =
-    do
-      x <- parse_t
-      parse_token End_token
-      return x
-  parse_flat :: Parser Accidental
-  parse_flat =
-    do
-      parse_token Flat_token
-      return Flat
-  parse_int :: Parser Int
-  parse_int = parse_negative_int <+> parse_nat
-  parse_length :: Parser Rat
-  parse_length = sum <$> parse_list Tie_token parse_simple_length
-  parse_list :: Token -> Parser t -> Parser [t]
-  parse_list separator parse_t = (:) <$> parse_t <*> parse_many (parse_element separator parse_t)
-  parse_many :: Parser t -> Parser [t]
-  parse_many parse_t = parse_empty <+> parse_some parse_t
-  parse_nat :: Parser Int
-  parse_nat = parse_token' nat_token
-  parse_natural :: Parser Accidental
-  parse_natural = return Natural
-  parse_natural_note_name :: Parser Natural_note_name
-  parse_natural_note_name = parse_token' note_name_token
-  parse_negative_int :: Parser Int
-  parse_negative_int =
-    do
-      parse_token Negation_token
-      i <- parse_nat
-      return (negate i)
-  parse_note :: Parser Note
-  parse_note =
-    do
-      note_name <- parse_note_name
-      octave <- parse_int
-      return (Note octave note_name)
-  parse_note_name :: Parser Note_name
-  parse_note_name = construct_note_name <$> (Note_name' <$> parse_natural_note_name <*> parse_accidental)
-  parse_notes :: Parser [Note]
-  parse_notes = parse_angular (parse_many parse_note)
-  parse_sequential :: Parser [Simultaneous]
-  parse_sequential = parse_many parse_simultaneous
-  parse_sharp :: Parser Accidental
-  parse_sharp =
-    do
-      parse_token Sharp_token
-      return Sharp
-  parse_simple_length :: Parser Rat
-  parse_simple_length =
-    do
-      base_length <- parse_base_length
-      dots <- parse_dots
-      return (base_length * (2 - 1 / 2 ^ dots))
-  parse_simultaneous :: Parser Simultaneous
-  parse_simultaneous = Simultaneous <$> parse_notes <*> parse_length
-  parse_some :: Parser t -> Parser [t]
-  parse_some parse_t = (:) <$> parse_t <*> parse_many parse_t
-  parse_token :: Token -> Parser ()
-  parse_token token = parse_token' (certain_token token)
-  parse_token' :: (Token -> Maybe t) -> Parser t
-  parse_token' f =
-    do
-      token : tokens <- get
-      case f token of
-        Nothing -> throwError ()
-        Just x ->
-          do
-            tell 1
-            put tokens
-            return x
-  runWS :: WS output state t -> state -> (t, state, output)
-  runWS f st = runRWS f () st
-  runWST :: WST output state f t -> state -> f (t, state, output)
-  runWST f = runRWST f ()
-  -- | The length of a note sequence.
-  sequential_length :: [Simultaneous] -> Rat
-  sequential_length sequential = sum (simultaneous_length <$> sequential)
-  -- | The length of a collection of notes.
-  simultaneous_length :: Simultaneous -> Rat
-  simultaneous_length (Simultaneous _ len) = len
-  -- | Discards the topmost division of the bar. For example, 1\/1 is transformed into 1\/2, 2\/2 into 1\/2, 3\/4 into 1\/4,
-  -- 4\/4 into 2\/4, 6\/8 into 3\/8, 9\/16 into 3\/16.
-  subdivision :: Time -> Time
-  subdivision (Time numerator denominator) =
-    case numerator of
-      [] -> Time [] (2 * denominator)
-      _ : numerator' -> Time numerator' denominator
-  template_error :: String -> t
-  template_error err = error ("Template error. " ++ err)
-  tokenise :: String -> [Token]
-  tokenise text =
-    let
-      ((), _, tokens) = runWS tokenise' (classify_char <$> text)
-    in
-      tokens
-  tokenise' :: Tokeniser ()
-  tokenise' =
-    do
-      maybe_char <- get_char 0
-      maybe_char' <- get_char 1
-      case maybe_char of
-        Nothing -> add_token End_token
-        Just char ->
-          do
-            case char of
-              Delimiter_char token ->
-                do
-                  add_token token
-                  next_char
-              Nat_char c ->
-                case (c, maybe_char') of
-                  ('0', Just (Nat_char _)) -> template_error "Int starting with zero."
-                  _ -> tokenise_nat
-              Negation_char ->
-                case maybe_char' of
-                  Just (Nat_char c) ->
-                    case c of
-                      '0' -> template_error "Negation of int starting with zero."
-                      _ ->
-                        do
-                          add_token Negation_token
-                          next_char
-                  _ -> template_error "Standalone negation sign."
-              Space_char -> next_char
-            tokenise'
-  tokenise_nat :: Tokeniser ()
-  tokenise_nat =
-    do
-      i <- gather_nat
-      add_token (Nat_token (read i))
-  wst :: (state -> f (t, state, output)) -> WST output state f t
-  wst f = RWST (\() -> f)
---------------------------------------------------------------------------------------------------------------------------------
+      Pitched x' -> f_pitched x'
+      Unpitched x' -> f_unpitched x'
+  -- | Scale the length of events. Mostly for internal use. ---
+  scale_lengths :: Ratio Int -> [Event' notes] -> [Event' notes] ---
+  scale_lengths x events = over #event_length ((*) x) <$> events ---
diff --git a/Composition/Parser.hs b/Composition/Parser.hs
new file mode 100644
--- /dev/null
+++ b/Composition/Parser.hs
@@ -0,0 +1,496 @@
+{-|
+Parse the custom file format for scores.
+-}
+module Composition.Parser (parse) where
+  import Composition.Errors
+  import Composition.Notes
+  import Composition.Score
+  import Composition.Time
+  import Control.Applicative
+  import Control.Monad.Except
+  import Control.Monad.Trans.Except
+  import Data.Char
+  import Data.Functor
+  import Data.List as List
+  import Data.Map.Strict
+  import Data.Maybe
+  import Data.Set
+  import Data.Tuple
+  import Parser.Files
+  import Parser.Locations
+  import Parser.Parser
+  import Parser.Utilities
+  import Text.Read
+  data Char_class =
+    Delimiter_char Token |
+    Invalid_char |
+    Letter_char Char |
+    Minus_char |
+    Nonzero_nat_char Char |
+    Newline_char |
+    Quote_char |
+    Whitespace_char |
+    Zero_char
+  type Parser = Parser' Token Error
+  data Token =
+    Clef_and_stave_token |
+    Clef_name_name_token |
+    Clef_name_value_token Clef_name |
+    Clef_token |
+    Clefs_and_staves_token |
+    Dot_token |
+    Eq_token |
+    Header_field_token Header_field |
+    Header_token |
+    Initial_position_token |
+    Instrument_clefs_and_staves_token |
+    Instrument_name_token |
+    Key_token |
+    Left_curly_bracket_token |
+    Left_square_bracket_token |
+    MIDI_instrument_token |
+    Negative_int_token Int |
+    Note_name_token Note_name |
+    Part_token |
+    Parts_token |
+    Percussion_clef_token |
+    Pitched_clef_token |
+    Pitched_token |
+    Positive_int_token Int |
+    Right_curly_bracket_token |
+    Right_square_bracket_token |
+    Score_token |
+    Slash_token |
+    Stave_groups_token |
+    Stave_token |
+    Tempo_token |
+    Text_token String |
+    Tie_token |
+    Time_name_token |
+    Time_value_token |
+    Title_token |
+    Transposition_token |
+    Unpitched_note_token |
+    Unpitched_token |
+    Velocity_token |
+    Zero_token
+  type Tokeniser = Tokeniser' Char_class Token Error
+  deriving instance Eq Char_class
+  deriving instance Eq Token
+  deriving instance Ord Token
+  deriving instance Show Char_class
+  deriving instance Show Token
+  classify_char :: Char -> Char_class
+  classify_char c =
+    case c of
+      '\n' -> Newline_char
+      ' ' -> Whitespace_char
+      _ | elem c "!#$%&'()*,:;<>?@^_`|~" || isLetter c -> Letter_char c
+      '"' -> Quote_char
+      _ | elem c (fst <$> delimiters) -> Delimiter_char (fromJust (List.lookup c delimiters))
+      '-' -> Minus_char
+      '0' -> Zero_char
+      _ | isDigit c && c /= '0' -> Nonzero_nat_char c
+      '=' -> Delimiter_char Eq_token
+      _ -> Invalid_char
+  clef_name_token :: Token -> Maybe Clef_name
+  clef_name_token token =
+    case token of
+      Clef_name_value_token clef_name -> Just clef_name
+      _ -> Nothing
+  construct_basic_length :: Int -> Either (Location -> Error) Basic_length
+  construct_basic_length len =
+    case denominator_to_basic_length len of
+      Nothing -> Left Invalid_note_length
+      Just len' -> Right len'
+  construct_header :: [(Header_field, String)] -> Either (Location -> Error) (Map Header_field String)
+  construct_header header =
+    case construct_map header of
+      Nothing -> Left Duplicate_header_fields
+      Just header' -> Right header'
+  construct_key :: [Note_name] -> Either (Location -> Error) (Set Note_name)
+  construct_key key =
+    case construct_set key of
+      Nothing -> Left Duplicate_note_names_in_key_signature
+      Just key' -> Right key'
+  construct_notes :: [Note note_type] -> Either (Location -> Error) (Notes note_type)
+  construct_notes notes =
+    case construct_set notes of
+      Nothing -> Left Duplicate_notes
+      Just notes' -> Right (Notes (notes'))
+  construct_word :: String -> Either (Location -> Error) Token
+  construct_word word =
+    (case construct_clef_name <|> construct_header_field <|> construct_keyword <|> construct_note_name' of
+      Nothing -> Left (Invalid_word word)
+      Just token -> Right token) where
+    construct_clef_name :: Maybe Token
+    construct_clef_name = Clef_name_value_token <$> readMaybe word
+    construct_header_field :: Maybe Token
+    construct_header_field = Header_field_token <$> readMaybe word
+    construct_keyword :: Maybe Token
+    construct_keyword =
+      case word of
+        "Clef_and_stave" -> Just Clef_and_stave_token
+        "Instrument_clefs_and_staves" -> Just Instrument_clefs_and_staves_token
+        "Part" -> Just Part_token
+        "Percussion_clef" -> Just Percussion_clef_token
+        "Pitched" -> Just Pitched_token
+        "Pitched_clef" -> Just Pitched_clef_token
+        "Score" -> Just Score_token
+        "Time" -> Just Time_value_token
+        "Unpitched" -> Just Unpitched_token
+        "clef" -> Just Clef_token
+        "clef_name" -> Just Clef_name_name_token
+        "clefs_and_staves" -> Just Clefs_and_staves_token
+        "header" -> Just Header_token
+        "initial_position" -> Just Initial_position_token
+        "instrument_name" -> Just Instrument_name_token
+        "key" -> Just Key_token
+        "midi_instrument" -> Just MIDI_instrument_token
+        "parts" -> Just Parts_token
+        "stave" -> Just Stave_token
+        "stave_groups" -> Just Stave_groups_token
+        "tempo" -> Just Tempo_token
+        "time" -> Just Time_name_token
+        "title" -> Just Title_token
+        "transposition" -> Just Transposition_token
+        "velocity" -> Just Velocity_token
+        "x" -> Just Unpitched_note_token
+        _ -> Nothing
+    construct_note_name' :: Maybe Token
+    construct_note_name' =
+      case word of
+        "" -> Nothing
+        natural_note_name : accidental ->
+          do
+            natural_note_name' <- read_natural_note_name natural_note_name
+            accidental' <- read_accidental accidental
+            Note_name_token <$> construct_note_name natural_note_name' accidental'
+  convert_midi_instrument :: Int -> Either (Location -> Error) MIDI_instrument
+  convert_midi_instrument midi_instrument =
+    do
+      check (Is_out_of_range_with_location MIDI_instrument_code_IOOR) (between 0 127 midi_instrument)
+      Right (fromIntegral midi_instrument)
+  convert_time_numerator_factor :: Int -> Either (Location -> Error) Time_numerator_factor
+  convert_time_numerator_factor time_numerator_factor =
+    case int_to_time_numerator_factor time_numerator_factor of
+      Nothing -> Left Invalid_time_numerator_factor
+      Just time_numerator_factor' -> Right time_numerator_factor'
+  convert_velocity :: Int -> Either (Location -> Error) Velocity
+  convert_velocity velocity =
+    do
+      check Velocity_out_of_range (between 0 (fromIntegral max_velocity) velocity)
+      Right (fromIntegral velocity)
+  delimiter_char :: Char_class -> Maybe Token
+  delimiter_char char_class =
+    case char_class of
+      Delimiter_char token -> Just token
+      _ -> Nothing
+  delimiters :: [(Char, Token)]
+  delimiters =
+    [
+      ('+', Tie_token),
+      ('.', Dot_token),
+      ('/', Slash_token),
+      ('=', Eq_token),
+      ('[', Left_square_bracket_token),
+      (']', Right_square_bracket_token),
+      ('{', Left_curly_bracket_token),
+      ('}', Right_curly_bracket_token)]
+  header_field_token :: Token -> Maybe Header_field
+  header_field_token token =
+    case token of
+      Header_field_token header_field -> Just header_field
+      _ -> Nothing
+  letter_char :: Char_class -> Maybe Char
+  letter_char char_class =
+    case char_class of
+      Letter_char c -> Just c
+      _ -> Nothing
+  nat_char :: Char_class -> Maybe Char
+  nat_char char_class =
+    case char_class of
+      Zero_char -> Just '0'
+      Nonzero_nat_char c -> Just c
+      _ -> Nothing
+  negative_int_token :: Token -> Maybe Int
+  negative_int_token token =
+    case token of
+      Negative_int_token i -> Just i
+      _ -> Nothing
+  next_location :: Char_class -> Location -> Location
+  next_location char_class =
+    case char_class of
+      Newline_char -> next_line
+      _ -> next_char
+  nonzero_nat_char :: Char_class -> Maybe Char
+  nonzero_nat_char char_class =
+    case char_class of
+      Nonzero_nat_char c -> Just c
+      _ -> Nothing
+  note_name_token :: Token -> Maybe Note_name
+  note_name_token token =
+    case token of
+      Note_name_token note_name -> Just note_name
+      _ -> Nothing
+  parse :: File_path -> ExceptT Error IO Score
+  parse file_path =
+    do
+      score <- read_file "aoi" readFile File_error file_path
+      except (fromJust (parse' classify_char next_location tokenise parse_score Parse_error score))
+  parse_basic_length :: Parser Basic_length
+  parse_basic_length = fmap_filter_parser construct_basic_length parse_positive_int
+  parse_clef_name :: Parser Clef_name
+  parse_clef_name = parse_token' clef_name_token
+  parse_curly_brackets :: Parser t -> Parser t
+  parse_curly_brackets = parse_brackets Left_curly_bracket_token Right_curly_bracket_token
+  parse_dot :: Parser Dot
+  parse_dot =
+    do
+      parse_token Dot_token
+      return Dot
+  parse_eq :: Parser ()
+  parse_eq = parse_token Eq_token
+  parse_field :: Token -> Parser t -> Parser t
+  parse_field name parse_t =
+    do
+      parse_token name
+      parse_eq
+      parse_t
+  parse_header :: Parser (Map Header_field String)
+  parse_header = fmap_filter_parser construct_header (parse_list' parse_header_field)
+  parse_header_field :: Parser (Header_field, String)
+  parse_header_field =
+    do
+      name <- parse_header_field_name
+      parse_eq
+      value <- parse_text
+      return (name, value)
+  parse_header_field_name :: Parser Header_field
+  parse_header_field_name = parse_token' header_field_token
+  parse_initial_position :: Parser Initial_position
+  parse_initial_position = parse_zero_initial_position <+> parse_nonzero_initial_position
+  parse_instrument_clefs_and_staves :: Parser (Pitched_or_unpitched Instrument_clefs_and_staves)
+  parse_instrument_clefs_and_staves =
+    (
+      Pitched <$> parse_instrument_clefs_and_staves' Branch_pitched <+>
+      Unpitched <$> parse_instrument_clefs_and_staves' Branch_unpitched)
+  parse_instrument_clefs_and_staves' :: forall note_type . Branch note_type -> Parser (Instrument_clefs_and_staves note_type)
+  parse_instrument_clefs_and_staves' branch =
+    do
+      parse_pitched_or_unpitched
+      parse_struct
+        Instrument_clefs_and_staves_token
+        (do
+          instrument_name <- parse_field Instrument_name_token parse_text
+          midi_instrument <- parse_field MIDI_instrument_token parse_midi_instrument
+          velocity <- parse_field Velocity_token parse_velocity
+          clefs_and_staves <- parse_field Clefs_and_staves_token (parse_list' parse_clef_and_stave)
+          return (Instrument_clefs_and_staves {instrument_name, midi_instrument, velocity, clefs_and_staves})) where
+    parse_clef :: Parser (Clef note_type)
+    parse_clef =
+      case branch of
+        Branch_pitched -> parse_pitched_clef
+        Branch_unpitched -> parse_unpitched_clef
+    parse_clef_and_stave :: Parser (Clef_and_stave note_type)
+    parse_clef_and_stave =
+      parse_struct
+        Clef_and_stave_token
+        (do
+          clef <- parse_field Clef_token parse_clef
+          stave <- parse_field Stave_token parse_stave
+          return (Clef_and_stave {clef, stave}))
+    parse_event :: Parser (Event note_type)
+    parse_event = parse_event' <+> parse_triplet
+    parse_event' :: Parser (Event note_type)
+    parse_event' = Event <$> parse_notes <*> parse_length
+    parse_events :: Parser [Event note_type]
+    parse_events = parse_list' parse_event
+    parse_note :: Parser (Note note_type)
+    parse_note =
+      case branch of
+        Branch_pitched -> parse_pitched_note
+        Branch_unpitched -> parse_unpitched_note
+    parse_notes :: Parser (Notes note_type)
+    parse_notes = parse_notes' <+> parse_tie
+    parse_notes' :: Parser (Notes note_type)
+    parse_notes' = fmap_filter_parser construct_notes (parse_list' parse_note)
+    parse_one_track :: Parser (Stave note_type)
+    parse_one_track = One_track <$> parse_events
+    parse_pitched_or_unpitched :: Parser ()
+    parse_pitched_or_unpitched = parse_token' pitched_or_unpitched_token
+    parse_stave :: Parser (Stave note_type)
+    parse_stave = parse_square_brackets (parse_one_track <+> parse_two_tracks)
+    parse_triplet :: Parser (Event note_type)
+    parse_triplet = Triplet <$> parse_events
+    parse_two_tracks :: Parser (Stave note_type)
+    parse_two_tracks = Two_tracks <$> parse_events <*> parse_events
+    pitched_or_unpitched_token :: Token -> Maybe ()
+    pitched_or_unpitched_token token =
+      case (branch, token) of
+        (Branch_pitched, Pitched_token) -> Just ()
+        (Branch_unpitched, Unpitched_token) -> Just ()
+        _ -> Nothing
+  parse_int :: Parser Int
+  parse_int = parse_negative_int <+> parse_nat
+  parse_key :: Parser Key
+  parse_key = fmap_filter_parser construct_key (parse_list' parse_note_name)
+  parse_length :: Parser Length
+  parse_length = Length <$> parse_basic_length <*> parse_many parse_dot
+  parse_list' :: Parser t -> Parser [t]
+  parse_list' parse_t = parse_square_brackets (parse_many parse_t)
+  parse_midi_instrument :: Parser MIDI_instrument
+  parse_midi_instrument = fmap_filter_parser convert_midi_instrument parse_nat
+  parse_nat :: Parser Int
+  parse_nat = parse_zero <+> parse_positive_int
+  parse_negative_int :: Parser Int
+  parse_negative_int = parse_token' negative_int_token
+  parse_nonzero_initial_position :: Parser Initial_position
+  parse_nonzero_initial_position =
+    do
+      num <- parse_positive_int
+      parse_slash
+      den <- parse_basic_length
+      return (Initial_position num den)
+  parse_note_name :: Parser Note_name
+  parse_note_name = parse_token' note_name_token
+  parse_part :: Parser Part
+  parse_part =
+    parse_struct
+      Part_token
+      (do
+        title <- parse_field Title_token parse_text
+        key <- parse_field Key_token parse_key
+        time <- parse_field Time_name_token parse_time
+        initial_position <- parse_field Initial_position_token parse_initial_position
+        tempo <- parse_field Tempo_token parse_positive_int
+        stave_groups <- parse_field Stave_groups_token (parse_list' (parse_list' parse_instrument_clefs_and_staves))
+        return (Part {title, key, time, initial_position, tempo, stave_groups}))
+  parse_pitched_clef :: Parser (Clef Pitched)
+  parse_pitched_clef =
+    parse_struct
+      Pitched_clef_token
+      (do
+        clef_name <- parse_field Clef_name_name_token parse_clef_name
+        transposition <- parse_field Transposition_token parse_int
+        return (Pitched_clef {clef_name, transposition}))
+  parse_pitched_note :: Parser (Note Pitched)
+  parse_pitched_note = flip Pitched_note <$> parse_note_name <*> parse_int
+  parse_positive_int :: Parser Int
+  parse_positive_int = parse_token' positive_int_token
+  parse_score :: Parser Score
+  parse_score =
+    parse_struct
+      Score_token
+      (do
+        title <- parse_field Title_token parse_text
+        header <- parse_field Header_token parse_header
+        parts <- parse_field Parts_token (parse_list' parse_part)
+        return (Score {title, header, parts}))
+  parse_slash :: Parser ()
+  parse_slash = parse_token Slash_token
+  parse_square_brackets :: Parser t -> Parser t
+  parse_square_brackets = parse_brackets Left_square_bracket_token Right_square_bracket_token
+  parse_struct :: Token -> Parser t -> Parser t
+  parse_struct name parse_fields =
+    do
+      parse_token name
+      parse_curly_brackets parse_fields
+  parse_text :: Parser String
+  parse_text = parse_token' text_token
+  parse_tie :: Parser (Notes note_type)
+  parse_tie =
+    do
+      parse_token Tie_token
+      return Tie
+  parse_time :: Parser Time
+  parse_time =
+    do
+      parse_token Time_value_token
+      num <- parse_list' parse_time_numerator_factor
+      den <- parse_basic_length
+      return (Time num den)
+  parse_time_numerator_factor :: Parser Time_numerator_factor
+  parse_time_numerator_factor = fmap_filter_parser convert_time_numerator_factor parse_positive_int
+  parse_unpitched_clef :: Parser (Clef Unpitched)
+  parse_unpitched_clef =
+    do
+      parse_token Percussion_clef_token
+      return Percussion_clef
+  parse_unpitched_note :: Parser (Note Unpitched)
+  parse_unpitched_note =
+    do
+      parse_token Unpitched_note_token
+      return Unpitched_note
+  parse_velocity :: Parser Velocity
+  parse_velocity = fmap_filter_parser convert_velocity parse_nat
+  parse_zero :: Parser Int
+  parse_zero =
+    do
+      parse_token Zero_token
+      return 0
+  parse_zero_initial_position :: Parser Initial_position
+  parse_zero_initial_position =
+    do
+      _ <- parse_zero
+      return (Initial_position 0 Whole)
+  positive_int_token :: Token -> Maybe Int
+  positive_int_token token =
+    case token of
+      Positive_int_token i -> Just i
+      _ -> Nothing
+  read_accidental :: String -> Maybe Accidental
+  read_accidental accidental = List.lookup accidental (swap <$> accidentals)
+  read_natural_note_name :: Char -> Maybe Natural_note_name
+  read_natural_note_name natural_note_name = readMaybe (natural_note_name : "_natural")
+  text_char :: Char_class -> Maybe Char
+  text_char char_class =
+    case char_class of
+      Delimiter_char token -> List.lookup token (swap <$> delimiters)
+      Invalid_char -> Nothing
+      Letter_char c -> Just c
+      Minus_char -> Just '-'
+      Nonzero_nat_char c -> Just c
+      Newline_char -> Nothing
+      Quote_char -> Nothing
+      Whitespace_char -> Just ' '
+      Zero_char -> Just '0'
+  text_token :: Token -> Maybe String
+  text_token token =
+    case token of
+      Text_token text -> Just text
+      _ -> Nothing
+  tokenise :: Tokeniser ()
+  tokenise = void (parse_many tokenise_1)
+  tokenise_1 :: Tokeniser ()
+  tokenise_1 =
+    tokenise_delimiter <+> tokenise_int <+> tokenise_newline <+> tokenise_text <+> tokenise_whitespace <+> tokenise_word
+  tokenise_delimiter :: Tokeniser ()
+  tokenise_delimiter = add_token (parse_token' delimiter_char)
+  tokenise_int :: Tokeniser ()
+  tokenise_int = add_token (tokenise_negative_int <+> tokenise_zero <+> tokenise_positive_int)
+  tokenise_negative_int :: Tokeniser Token
+  tokenise_negative_int =
+    do
+      parse_token Minus_char
+      i <- tokenise_positive_int'
+      return (Negative_int_token (negate i))
+  tokenise_newline :: Tokeniser ()
+  tokenise_newline = parse_token Newline_char
+  tokenise_positive_int :: Tokeniser Token
+  tokenise_positive_int = Positive_int_token <$> tokenise_positive_int'
+  tokenise_positive_int' :: Tokeniser Int
+  tokenise_positive_int' = read <$> ((:) <$> parse_token' nonzero_nat_char <*> parse_many (parse_token' nat_char))
+  tokenise_text :: Tokeniser ()
+  tokenise_text = add_token (Text_token <$> tokenise_quotes (parse_many (parse_token' text_char)))
+  tokenise_quotes :: Tokeniser t -> Tokeniser t
+  tokenise_quotes = parse_brackets Quote_char Quote_char
+  tokenise_whitespace :: Tokeniser ()
+  tokenise_whitespace = parse_token Whitespace_char
+  tokenise_word :: Tokeniser ()
+  tokenise_word = add_token (fmap_filter_parser construct_word (parse_some (parse_token' letter_char)))
+  tokenise_zero :: Tokeniser Token
+  tokenise_zero =
+    do
+      parse_token Zero_char
+      return Zero_token
diff --git a/Composition/Score.hs b/Composition/Score.hs
new file mode 100644
--- /dev/null
+++ b/Composition/Score.hs
@@ -0,0 +1,301 @@
+{-| ---
+Description: Lilypond + MIDI capable scores. ---
+ ---
+* Lilypond + MIDI capable scores ---
+-} ---
+module Composition.Score ( ---
+  Clef (..), ---
+  Clef_and_stave (..), ---
+  Clef_name (..), ---
+  Header_field (..), ---
+  Instrument_clefs_and_staves (..), ---
+  Key, ---
+  MIDI_instrument, ---
+  Part (..), ---
+  Score (..), ---
+  Stave (..), ---
+  Tempo, ---
+  Track (..), ---
+  Velocity, ---
+  max_velocity, ---
+  notate, ---
+  remove_notational_information, ---
+  stave_to_events, ---
+  tracks_length) where ---
+  import Composition.Errors ---
+  import Composition.Notes ---
+  import Composition.Theory ---
+  import Composition.Time ---
+  import Control.Monad ---
+  import Data.Fixed ---
+  import Data.Foldable ---
+  import Data.Functor.Barbie ---
+  import Data.Map.Strict ---
+  import Data.Maybe ---
+  import Data.Ratio ---
+  import Data.Set as Set ---
+  import Data.Word ---
+  import GHC.Real ---
+  import Parser.Utilities ---
+  -- | Clefs (with transposition). ---
+  data Clef note_type where ---
+    Pitched_clef :: {clef_name :: Clef_name, transposition :: Steps} -> Clef Pitched ---
+    Percussion_clef :: Clef Unpitched ---
+  -- | Clef names. ---
+  data Clef_name = Subbass | Bass | Baritone_F | Baritone_C | Tenor | Alto | Mezzosoprano | Soprano | Treble | French ---
+  -- | Stave with clef. ---
+  data Clef_and_stave note_type = Clef_and_stave {clef :: Clef note_type, stave :: Stave note_type} ---
+  -- Score header fields. ---
+  data Header_field = Composer | Instrument | Subtitle ---
+  -- | A group of staves for one instrument. ---
+  data Instrument_clefs_and_staves note_type = ---
+    Instrument_clefs_and_staves { ---
+      instrument_name :: String, ---
+      midi_instrument :: MIDI_instrument, ---
+      velocity :: Velocity, ---
+      clefs_and_staves :: [Clef_and_stave note_type]} ---
+  -- | A set of note names that serves as a key signature. For example, the key signature of C minor would be @[E_flat, A_flat, ---
+  -- B_flat]@. Naturals and conflicting accidentals will result in an error. ---
+  type Key = Set Note_name ---
+  -- MIDI instrument code. ---
+  type MIDI_instrument = Word8 ---
+  data Notate_notes note_type = Notate_rest | Notate_notes (Set (Note note_type)) | Notate_tie ---
+  -- | Each part can have a different key, time signature and instrumentation. ---
+  data Part = ---
+    Part { ---
+      title :: String, ---
+      key :: Key, ---
+      time :: Time, ---
+      initial_position :: Initial_position, ---
+      tempo :: Tempo, ---
+      stave_groups :: [[Pitched_or_unpitched Instrument_clefs_and_staves]]} ---
+  -- A Lilypond + MIDI capable score. ---
+  data Score = Score {title :: String, header :: Map Header_field String, parts :: [Part]} ---
+  -- | A stave with one or two voices. Rhythmically independent voices require separate tracks. ---
+  data Stave note_type = One_track [Event note_type] | Two_tracks [Event note_type] [Event note_type] ---
+  -- | Tempo in beats per minute. ---
+  type Tempo = Int ---
+  -- | Tracks stripped of notational information (brackets, instrument names, clefs and note length display information). Mostly ---
+  -- for internal use. ---
+  data Track note_type = Track {instrument :: MIDI_instrument, velocity :: Velocity, events :: [Event_fraction note_type]} ---
+  -- | MIDI volume ranges from 0 to 127. ---
+  type Velocity = Word8 ---
+  deriving instance Eq Clef_name ---
+  deriving instance Eq Header_field ---
+  deriving instance Ord Clef_name ---
+  deriving instance Ord Header_field ---
+  deriving instance Read Clef_name
+  deriving instance Read Header_field
+  deriving instance Show (Clef note_type) ---
+  deriving instance Show (Clef_and_stave note_type) ---
+  deriving instance Show Clef_name ---
+  deriving instance Show Header_field ---
+  deriving instance Show (Instrument_clefs_and_staves note_type) ---
+  deriving instance Show (Notate_notes note_type) ---
+  deriving instance Show Part ---
+  deriving instance Show Score ---
+  deriving instance Show (Stave note_type) ---
+  deriving instance Show (Track note_type) ---
+  check_length :: Event' notes -> Either Error () ---
+  check_length (Event' _ (num :% den)) = ---
+    do ---
+      check Non_positive_note_length (0 < num) ---
+      check Note_length_denominator_contains_factors_other_than_2_and_3 (is_smooth [2, 3] den) ---
+      return () ---
+  is_smooth :: [Int] -> Int -> Bool ---
+  is_smooth factors i = ---
+    case i of ---
+      1 -> True ---
+      _ -> ---
+        case factors of ---
+          [] -> False ---
+          factor : factors' -> is_smooth factors' (remove_factor factor i) ---
+  join_events :: [Event' (Notes note_type)] -> [Event_fraction note_type] ---
+  join_events events = ---
+    case events of ---
+      [] -> [] ---
+      Event' notes len : events' -> join_events' (Event' (notes_to_set notes) len) events' ---
+  join_events' :: Event_fraction note_type -> [Event' (Notes note_type)] -> [Event_fraction note_type] ---
+  join_events' (Event' notes_0 len_0) events = ---
+    case events of ---
+      [] -> [Event' notes_0 len_0] ---
+      Event' maybe_notes_1 len_1 : events' -> ---
+        case join_notes notes_0 maybe_notes_1 of ---
+          Nothing -> join_events' (Event' notes_0 (len_0 + len_1)) events' ---
+          Just notes_1' -> Event' notes_0 len_0 : join_events' (Event' notes_1' len_1) events' ---
+  join_notes :: Set (Note note_type) -> Notes note_type -> Maybe (Set (Note note_type)) ---
+  join_notes notes_0 maybe_notes_1 = ---
+    case maybe_notes_1 of ---
+      Notes notes_1 -> ---
+        case (Set.elems notes_0, Set.elems notes_1) of ---
+          ([], []) -> Nothing ---
+          _ -> Just notes_1 ---
+      Tie -> Nothing ---
+  -- | Maximum MIDI volume. ---
+  max_velocity :: Velocity ---
+  max_velocity = 127 ---
+  -- | Add note length display information to events. Mostly for internal use. ---
+  notate :: Time -> Initial_position -> [Event_fraction note_type] -> Either Error [Event note_type] ---
+  notate time initial_position events = ---
+    do ---
+      traverse_ check_length events ---
+      notate_events time (initial_position_to_fraction initial_position) (transform_event <$> events) ---
+  notate_basic_length :: Length_fraction -> Maybe Basic_length ---
+  notate_basic_length (num :% den) = ---
+    case num of ---
+      1 -> ---
+        case den of ---
+          1 -> Just Whole ---
+          2 -> Just Half ---
+          4 -> Just Quarter ---
+          8 -> Just Eighth ---
+          16 -> Just Sixteenth ---
+          32 -> Just Thirty_second ---
+          64 -> Just Sixty_fourth ---
+          128 -> Just One_hundred_and_twenty_eighth ---
+          _ -> Nothing ---
+      _ -> Nothing ---
+  notate_binary_block :: Time -> (Position, [Event' (Notate_notes note_type)]) -> Either Error [Event note_type] ---
+  notate_binary_block time (position, events) = ---
+    case events of ---
+      [event] -> notate_event time position event ---
+      _ -> ---
+        let ---
+          num :% den = events_length events in ---
+          ( ---
+            (\ evs -> [Triplet evs]) <$> ---
+            case num of ---
+              1 -> ---
+                case denominator_to_basic_length (2 * den) of ---
+                  Nothing -> Left (Is_out_of_range_without_location Note_length_denominator_IOOR) ---
+                  Just den' -> notate_measure (Time [Three] den') (0, scale_lengths (3 % 2) events) ---
+              _ -> ---
+                do ---
+                  time' <- time_subdivision time ---
+                  notate_events time' (mod' position (measure_length time')) events) ---
+  notate_event :: Time -> Position -> Event' (Notate_notes note_type) -> Either Error [Event note_type] ---
+  notate_event time position event@(Event' notes len) = ---
+    case notate_simple_length len of ---
+      Nothing -> ---
+        do ---
+          time' <- time_subdivision time ---
+          notate_events time' (mod' position (measure_length time')) [event] ---
+      Just len' -> Right [Event (notate_notes notes) len'] ---
+  notate_events :: Time -> Position -> [Event' (Notate_notes note_type)] -> Either Error [Event note_type] ---
+  notate_events time position events = join <$> traverse (notate_measure time) (separate_measures time position events) ---
+  notate_measure :: Time -> (Position, [Event' (Notate_notes note_type)]) -> Either Error [Event note_type] ---
+  notate_measure time (position, events) = ---
+    do ---
+      binary_blocks <- separate_binary_blocks time position events ---
+      join <$> traverse (notate_binary_block time) binary_blocks ---
+  notate_notes :: Notate_notes note_type -> Notes note_type ---
+  notate_notes notes = ---
+    case notes of ---
+      Notate_rest -> Notes Set.empty ---
+      Notate_notes notes' -> Notes notes' ---
+      Notate_tie -> Tie ---
+  notate_simple_length :: Length_fraction -> Maybe Length ---
+  notate_simple_length (num :% den) = ---
+    do ---
+      dots <- (\ i -> i - 1) <$> lg (1 + num) ---
+      basic_length <- notate_basic_length (2 ^ dots % den) ---
+      Just (Length basic_length (replicate dots Dot)) ---
+  notes_tie :: Notate_notes note_type -> Notate_notes note_type ---
+  notes_tie notes = ---
+    case notes of ---
+      Notate_rest -> Notate_rest ---
+      Notate_notes _ -> Notate_tie ---
+      Notate_tie -> Notate_tie ---
+  notes_to_set :: Notes note_type -> Set (Note note_type) ---
+  notes_to_set maybe_notes = ---
+    case maybe_notes of ---
+      Notes notes -> notes ---
+      Tie -> Set.empty ---
+  remove_factor :: Int -> Int -> Int ---
+  remove_factor factor i = ---
+    case mod i factor of ---
+      0 -> remove_factor factor (div i factor) ---
+      _ -> i ---
+  -- | Strip tracks of notational information (brackets, instrument names, clefs, note length display information). Mostly for ---
+  -- internal use. ---
+  remove_notational_information :: [[Pitched_or_unpitched Instrument_clefs_and_staves]] -> [Pitched_or_unpitched Track] ---
+  remove_notational_information stave_groups = ---
+    stave_groups >>= flip (>>=) (btraverse remove_notational_information_instrument_clefs_and_staves) ---
+  remove_notational_information_clef_and_stave :: Clef_and_stave note_type -> [[Event_fraction note_type]] ---
+  remove_notational_information_clef_and_stave (Clef_and_stave {stave}) = ---
+    remove_notational_information_events <$> stave_to_events stave ---
+  remove_notational_information_events :: [Event note_type] -> [Event_fraction note_type] ---
+  remove_notational_information_events events = join_events (remove_notational_information_events' events) ---
+  remove_notational_information_events' :: [Event note_type] -> [Event' (Notes note_type)] ---
+  remove_notational_information_events' events = events >>= remove_notational_information_event ---
+  remove_notational_information_event :: Event note_type -> [Event' (Notes note_type)] ---
+  remove_notational_information_event event = ---
+    case event of ---
+      Event notes len -> [Event' notes (length_to_fraction len)] ---
+      Triplet events -> scale_lengths (2 % 3) (remove_notational_information_events' events) ---
+  remove_notational_information_instrument_clefs_and_staves :: Instrument_clefs_and_staves note_type -> [Track note_type] ---
+  remove_notational_information_instrument_clefs_and_staves ---
+    (Instrument_clefs_and_staves {midi_instrument, velocity, clefs_and_staves}) = ---
+      ( ---
+        (\ events -> Track {instrument = midi_instrument, velocity, events}) <$> ---
+        (clefs_and_staves >>= remove_notational_information_clef_and_stave)) ---
+  separate_binary_blocks :: ---
+    Time -> Position -> [Event' (Notate_notes note_type)] -> Either Error [(Position, [Event' (Notate_notes note_type)])] ---
+  separate_binary_blocks time position events = ---
+    case events of ---
+      [] -> Right [] ---
+      event@(Event' _ len) : events' -> ---
+        do ---
+          let position' = position + len ---
+          case is_smooth [2] (denominator position') of ---
+            False -> ---
+              do ---
+                binary_blocks <- separate_binary_blocks time position' events' ---
+                case binary_blocks of ---
+                  [] -> Left Track_ends_with_an_incomplete_triplet ---
+                  (_, events'') : binary_blocks' -> Right ((position, event : events'') : binary_blocks') ---
+            True -> (:) (position, [event]) <$> separate_binary_blocks time position' events' ---
+  separate_measures :: ---
+    Time -> Position -> [Event' (Notate_notes note_type)] -> [(Length_fraction, [Event' (Notate_notes note_type)])] ---
+  separate_measures time initial_position events = ---
+    case separate_measures' time initial_position events of ---
+      [] -> [] ---
+      measure : measures' -> (initial_position, measure) : ((,) 0 <$> measures') ---
+  separate_measures' :: Time -> Position -> [Event' (Notate_notes note_type)] -> [[Event' (Notate_notes note_type)]] ---
+  separate_measures' time position events = ---
+    case events of ---
+      [] -> [] ---
+      Event' notes len : events' -> ---
+        let ---
+          position' = position + len in ---
+          case compare position' (measure_length time) of ---
+            LT -> ---
+              case separate_measures' time position' events' of ---
+                [] -> [[Event' notes len]] ---
+                measure : measures -> (Event' notes len : measure) : measures ---
+            EQ -> [Event' notes len] : separate_measures' time 0 events' ---
+            GT -> ---
+              let ---
+                len' = measure_length time - position in ---
+                [Event' notes len'] : separate_measures' time 0 (Event' (notes_tie notes) (len - len') : events') ---
+  -- | Convert the stave to a list of voices. Mostly for internal use. ---
+  stave_to_events :: Stave note_type -> [[Event note_type]] ---
+  stave_to_events stave = ---
+    case stave of ---
+      One_track events -> [events] ---
+      Two_tracks events_0 events_1 -> [events_0, events_1] ---
+  track_length :: Track note_type -> Ratio Int ---
+  track_length (Track {events}) = events_length events ---
+  -- | Calculate part length. Returns Nothing if there is a track length mismatch. ---
+  tracks_length :: [Pitched_or_unpitched Track] -> Either Error Length_fraction ---
+  tracks_length tracks = ---
+    case all_equal (pitched_and_unpitched track_length <$> tracks) of ---
+      Nothing -> Left Track_length_mismatch ---
+      Just maybe_len -> Right (fromMaybe 0 maybe_len) ---
+  transform_event :: Event_fraction note_type -> Event' (Notate_notes note_type) ---
+  transform_event (Event' notes len) = Event' (transform_notes notes) len ---
+  transform_notes :: Set (Note note_type) -> Notate_notes note_type ---
+  transform_notes notes = ---
+    case Set.elems notes of ---
+      [] -> Notate_rest ---
+      _ -> Notate_notes notes ---
diff --git a/Composition/Theory.hs b/Composition/Theory.hs
--- a/Composition/Theory.hs
+++ b/Composition/Theory.hs
@@ -1,115 +1,179 @@
---------------------------------------------------------------------------------------------------------------------------------
-{-# OPTIONS_GHC -Wall #-}
-{-# LANGUAGE StandaloneDeriving #-}
 {-|
-Description: A module for basic music theory.
-
-This module contains functions for computing intervals and classifying chords.
+Basic music theory.
 -}
 module Composition.Theory (
+  Interval (..),
   Interval_name (..),
-  Interval_number (..),
-  Interval_quality (..),
-  accidental_semitones,
-  compute_interval_name,
+  Semitones,
+  Steps,
+  distance_in_semitones,
+  distance_in_steps,
+  find_interval,
+  interval_to_semitones,
+  interval_to_steps,
+  intervals_enharmonic,
   invert_interval_name,
-  semitones_from_c,
-  steps_from_c) where
-  import Composition.Notes (Accidental (..), Natural_note_name (..), Note_name, Note_name' (..), deconstruct_note_name)
-  -- | Interval name.
-  data Interval_name = Interval_name Interval_quality Interval_number
-  -- | Interval numbers.
-  data Interval_number = Prime | Second | Third | Fourth | Fifth | Sixth | Seventh
-  -- | Interval qualities, allowing for up to three levels of augmentation.
-  data Interval_quality =
-    Thrice_diminished | Twice_diminished | Diminished | Minor | Perfect | Major | Augmented | Twice_augmented | Thrice_augmented
-  deriving instance Enum Interval_number
-  deriving instance Eq Interval_number
-  deriving instance Ord Interval_number
+  notes_enharmonic,
+  semitones_from_c) where
+  import Composition.Notes
+  import Data.Maybe
+  import Data.Tuple
+  -- | Intervals.
+  data Interval = Interval Octave Interval_name
+  -- | Interval names.
+  data Interval_name =
+    Twice_diminished_prime |
+    Diminished_prime |
+    Perfect_prime |
+    Diminished_second |
+    Augmented_prime |
+    Minor_second |
+    Twice_augmented_prime |
+    Major_second |
+    Diminished_third |
+    Augmented_second |
+    Minor_third |
+    Twice_diminished_fourth |
+    Twice_augmented_second |
+    Major_third |
+    Diminished_fourth |
+    Augmented_third |
+    Perfect_fourth |
+    Twice_diminished_fifth |
+    Augmented_fourth |
+    Diminished_fifth |
+    Twice_augmented_fourth |
+    Perfect_fifth |
+    Diminished_sixth |
+    Augmented_fifth |
+    Minor_sixth |
+    Twice_diminished_seventh |
+    Twice_augmented_fifth |
+    Major_sixth |
+    Diminished_seventh |
+    Augmented_sixth |
+    Minor_seventh |
+    Major_seventh |
+    Augmented_seventh
+  -- | Distance in semitones.
+  type Semitones = Int
+  -- | Distance in steps.
+  type Steps = Int
+  deriving instance Eq Interval
+  deriving instance Eq Interval_name
+  deriving instance Show Interval
   deriving instance Show Interval_name
-  deriving instance Show Interval_number
-  deriving instance Show Interval_quality
-  -- | Returns the number of semitones by which the accidental adjusts the pitch.
-  accidental_semitones :: Accidental -> Int
-  accidental_semitones accidental =
+  accidental_to_semitones :: Accidental -> Semitones
+  accidental_to_semitones accidental =
     case accidental of
       Flat -> -1
       Natural -> 0
       Sharp -> 1
-  -- | Computes the interval between two note names.
-  compute_interval_name :: Note_name -> Note_name -> Interval_name
-  compute_interval_name note_name_0 note_name_1 =
+  construct_interval_name :: Semitones -> Steps -> Interval_name
+  construct_interval_name semitones steps = fromJust (lookup (semitones, steps) (swap <$> interval_names))
+  deconstruct_interval_name :: Interval_name -> (Semitones, Steps)
+  deconstruct_interval_name interval_name = fromJust (lookup interval_name interval_names)
+  -- | The distance in semitones between two notes.
+  distance_in_semitones :: Note Pitched -> Note Pitched -> Semitones
+  distance_in_semitones note_0 note_1 = interval_to_semitones (find_interval note_0 note_1)
+  -- | The distance in steps between two notes.
+  distance_in_steps :: Note Pitched -> Note Pitched -> Steps
+  distance_in_steps note_0 note_1 = interval_to_steps (find_interval note_0 note_1)
+  -- | Note that this function assumes that the inputs are ordered. If the inputs are not ordered the function will return the
+  -- complement interval with a negative octave number.
+  find_interval :: Note Pitched -> Note Pitched -> Interval
+  find_interval (Pitched_note octave_0 note_name_0) (Pitched_note octave_1 note_name_1) =
+    Interval
+      (
+        octave_1 -
+        octave_0 -
+        case compare (steps_from_c note_name_0) (steps_from_c note_name_1) of
+          (LT; EQ) -> 0
+          GT -> 1)
+      (find_interval_name note_name_0 note_name_1)
+  find_interval_name :: Note_name -> Note_name -> Interval_name
+  find_interval_name note_name_0 note_name_1 =
     case compare note_name_0 note_name_1 of
-      LT -> compute_interval_name' note_name_0 note_name_1
-      EQ -> Interval_name Perfect Prime
-      GT -> invert_interval_name (compute_interval_name' note_name_1 note_name_0)
-  compute_interval_name' :: Note_name -> Note_name -> Interval_name
-  compute_interval_name' note_name_0 note_name_1 =
-    let
-      interval_number = toEnum (mod (steps_from_c note_name_1 - steps_from_c note_name_0) 7)
-    in
-      Interval_name
-        (compute_interval_quality interval_number (semitones_from_c note_name_1 - semitones_from_c note_name_0))
-        interval_number
-  compute_interval_quality :: Interval_number -> Int -> Interval_quality
-  compute_interval_quality interval_number =
-    case interval_number of
-      Prime -> compute_perfect_interval_quality 0
-      Second -> compute_minor_or_major_interval_quality 1
-      Third -> compute_minor_or_major_interval_quality 3
-      Fourth -> compute_perfect_interval_quality 5
-      Fifth -> compute_perfect_interval_quality 7
-      Sixth -> compute_minor_or_major_interval_quality 8
-      Seventh -> compute_minor_or_major_interval_quality 10
-  compute_minor_or_major_interval_quality :: Int -> Int -> Interval_quality
-  compute_minor_or_major_interval_quality minor_semitones semitones =
-    case mod (semitones - minor_semitones) 12 of
-      0 -> Minor
-      1 -> Major
-      2 -> Augmented
-      3 -> Twice_augmented
-      4 -> Thrice_augmented
-      9 -> Thrice_diminished
-      10 -> Twice_diminished
-      11 -> Diminished
-      _ -> undefined
-  compute_perfect_interval_quality :: Int -> Int -> Interval_quality
-  compute_perfect_interval_quality perfect_semitones semitones =
-    case semitones - perfect_semitones of
-      -3 -> Thrice_diminished
-      -2 -> Twice_diminished
-      -1 -> Diminished
-      0 -> Perfect
-      1 -> Augmented
-      2 -> Twice_augmented
-      3 -> Thrice_augmented
-      _ -> undefined
-  -- | Inverts the interval name.
+      LT -> find_interval_name' note_name_0 note_name_1
+      EQ -> Perfect_prime
+      GT -> invert_interval_name (find_interval_name' note_name_1 note_name_0)
+  find_interval_name' :: Note_name -> Note_name -> Interval_name
+  find_interval_name' note_name_0 note_name_1 =
+    construct_interval_name
+      (semitones_from_c note_name_1 - semitones_from_c note_name_0)
+      (steps_from_c note_name_1 - steps_from_c note_name_0)
+  interval_name_to_semitones :: Interval_name -> Semitones
+  interval_name_to_semitones interval_name = fst (deconstruct_interval_name interval_name)
+  interval_name_to_steps :: Interval_name -> Steps
+  interval_name_to_steps interval_name = snd (deconstruct_interval_name interval_name)
+  interval_names :: [(Interval_name, (Semitones, Steps))]
+  interval_names =
+    [
+      (Twice_diminished_prime, (-2, 0)),
+      (Diminished_prime, (-1, 0)),
+      (Perfect_prime, (0, 0)),
+      (Diminished_second, (0, 1)),
+      (Augmented_prime, (1, 0)),
+      (Minor_second, (1, 1)),
+      (Twice_augmented_prime, (2, 0)),
+      (Major_second, (2, 1)),
+      (Diminished_third, (2, 2)),
+      (Augmented_second, (3, 1)),
+      (Minor_third, (3, 2)),
+      (Twice_diminished_fourth, (3, 3)),
+      (Twice_augmented_second, (4, 1)),
+      (Major_third, (4, 2)),
+      (Diminished_fourth, (4, 3)),
+      (Augmented_third, (5, 2)),
+      (Perfect_fourth, (5, 3)),
+      (Twice_diminished_fifth, (5, 4)),
+      (Augmented_fourth, (6, 3)),
+      (Diminished_fifth, (6, 4)),
+      (Twice_augmented_fourth, (7, 3)),
+      (Perfect_fifth, (7, 4)),
+      (Diminished_sixth, (7, 5)),
+      (Augmented_fifth, (8, 4)),
+      (Minor_sixth, (8, 5)),
+      (Twice_diminished_seventh, (8, 6)),
+      (Twice_augmented_fifth, (9, 4)),
+      (Major_sixth, (9, 5)),
+      (Diminished_seventh, (9, 6)),
+      (Augmented_sixth, (10, 5)),
+      (Minor_seventh, (10, 6)),
+      (Major_seventh, (11, 6)),
+      (Augmented_seventh, (12, 6))]
+  -- | The size of an interval in semitones.
+  interval_to_semitones :: Interval -> Semitones
+  interval_to_semitones (Interval octave interval_name) = 12 * octave + interval_name_to_semitones interval_name
+  -- | The size of an interval in steps.
+  interval_to_steps :: Interval -> Steps
+  interval_to_steps (Interval octave interval_name) = 7 * octave + interval_name_to_steps interval_name
+  -- | Checks whether two intervals are enharmonic.
+  intervals_enharmonic :: Interval -> Interval -> Bool
+  intervals_enharmonic interval_0 interval_1 = interval_to_semitones interval_0 == interval_to_semitones interval_1
+  -- | Invert interval name.
   invert_interval_name :: Interval_name -> Interval_name
-  invert_interval_name (Interval_name quality interval_number) =
-    Interval_name (invert_interval_quality quality) (invert_interval_number interval_number)
-  invert_interval_quality :: Interval_quality -> Interval_quality
-  invert_interval_quality quality =
-    case quality of
-      Thrice_diminished -> Thrice_augmented
-      Twice_diminished -> Twice_augmented
-      Diminished -> Augmented
-      Minor -> Major
-      Perfect -> Perfect
-      Major -> Minor
-      Augmented -> Diminished
-      Twice_augmented -> Twice_diminished
-      Thrice_augmented -> Thrice_diminished
-  invert_interval_number :: Interval_number -> Interval_number
-  invert_interval_number interval_number = toEnum (mod (negate (fromEnum interval_number)) 7)
-  -- | Computes the number of semitones from C. C♭ is considered to be -1 semitone from C and B♯ 12 semitones from C.
-  semitones_from_c :: Note_name -> Int
+  invert_interval_name interval_name =
+    let
+      (semitones, steps) = deconstruct_interval_name interval_name in
+      construct_interval_name (invert_interval_semitones steps semitones) (invert_interval_steps steps)
+  invert_interval_semitones :: Steps -> Semitones -> Semitones
+  invert_interval_semitones steps semitones =
+    case steps of
+      0 -> negate semitones
+      _ -> 12 - semitones
+  invert_interval_steps :: Steps -> Steps
+  invert_interval_steps steps = mod (negate steps) 7
+  -- | Checks whether two notes are enharmonic.
+  notes_enharmonic :: Note Pitched -> Note Pitched -> Bool
+  notes_enharmonic note_0 note_1 = 0 == distance_in_semitones note_0 note_1
+  -- | Distance from C in semitones.
+  semitones_from_c :: Note_name -> Semitones
   semitones_from_c note_name =
     let
-      Note_name' natural_note_name accidental = deconstruct_note_name note_name
-    in
-      semitones_from_c_natural natural_note_name + accidental_semitones accidental
-  semitones_from_c_natural :: Natural_note_name -> Int
+      (natural_note_name, accidental) = deconstruct_note_name note_name in
+      semitones_from_c_natural natural_note_name + accidental_to_semitones accidental
+  semitones_from_c_natural :: Natural_note_name -> Semitones
   semitones_from_c_natural natural_note_name =
     case natural_note_name of
       C_natural -> 0
@@ -119,11 +183,5 @@
       G_natural -> 7
       A_natural -> 9
       B_natural -> 11
-  -- | Computes the number of steps from C.
-  steps_from_c :: Note_name -> Int
-  steps_from_c note_name =
-    let
-      Note_name' natural_note_name _ = deconstruct_note_name note_name
-    in
-      fromEnum natural_note_name
---------------------------------------------------------------------------------------------------------------------------------
+  steps_from_c :: Note_name -> Steps
+  steps_from_c note_name = fromEnum (fst (deconstruct_note_name note_name))
diff --git a/Composition/Time.hs b/Composition/Time.hs
new file mode 100644
--- /dev/null
+++ b/Composition/Time.hs
@@ -0,0 +1,70 @@
+{-| ---
+Description: Rhythmic organisation. ---
+ ---
+* Time signatures ---
+* Note positions ---
+* Measure subdivisions ---
+-} ---
+module Composition.Time ( ---
+  Initial_position (..), ---
+  Position, ---
+  Time (..), ---
+  Time_numerator_factor (..), ---
+  initial_position_to_fraction, ---
+  int_to_time_numerator_factor,
+  measure_length, ---
+  time_numerator_factor_to_int, ---
+  time_numerator_to_int, ---
+  time_subdivision) where ---
+  import Composition.Errors
+  import Composition.Notes
+  import Data.Maybe
+  import Data.Ratio
+  import Data.Tuple
+  -- | The timing of the first event relative to the start of the measure. For example, if the piece is in 3/4 and starts with a ---
+  -- 1/4 pickup bar, the initial position can be written as @Initial_position 1 Half@ or @Initial_position 2 Quarter@. If the ---
+  -- piece starts with a full bar, the numerator is 0 (and you can use any length as the denominator). ---
+  data Initial_position = Initial_position Int Basic_length ---
+  -- | The position of an event relative to the start of the measure. Mostly for internal use. ---
+  type Position = Ratio Int ---
+  -- | Time signature numerators are written as a list of factors. For example, 2 is written as @[Two]@, 3 as @[Three]@, 4 as ---
+  -- @[Two Two]@, 6 as @[Two Three]@, 8 as @[Two Two Two]@, 9 as @[Three Three]@, 12 as @[Two Two Three]@, and so on. Note that ---
+  -- in some applications the order matters. For example, 18 as @[Three Two Three]@ means that the bar is divided into three ---
+  -- parts, each part is divided in half and then each half is divided into three parts. But 18 as @[Two Three Three]@ means ---
+  -- that the bar is divided in half, each half is divided into three parts and then each part is divided into three parts. This ---
+  -- will affect the placement of ties in keyboard transcriptions. ---
+  data Time_numerator_factor = Two | Three ---
+  -- | Time signatures. For example, 2/2 is written as @Time [Two] Half@, 3/2 as @Time [Three] Half@, 2/4 as @Time [Two] ---
+  -- Quarter@, and so on. ---
+  data Time = Time [Time_numerator_factor] Basic_length ---
+  deriving instance Eq Time_numerator_factor
+  deriving instance Show Initial_position
+  deriving instance Show Time
+  deriving instance Show Time_numerator_factor
+  -- | Convert initial position to fraction. Mostly for internal use. ---
+  initial_position_to_fraction :: Initial_position -> Position ---
+  initial_position_to_fraction (Initial_position num den) = fromIntegral num * basic_length_to_fraction den ---
+  -- | Convert int to time numerator factor. Mostly for internal use.
+  int_to_time_numerator_factor :: Int -> Maybe Time_numerator_factor
+  int_to_time_numerator_factor time_numerator_factor = lookup time_numerator_factor (swap <$> time_numerator_factors)
+  -- | The length of one measure. ---
+  measure_length :: Time -> Length_fraction ---
+  measure_length (Time num den) = fromIntegral (time_numerator_to_int num) * basic_length_to_fraction den ---
+  -- | Convert time numerator factor to int. Mostly for internal use.
+  time_numerator_factor_to_int :: Time_numerator_factor -> Int
+  time_numerator_factor_to_int time_numerator_factor = fromJust (lookup time_numerator_factor time_numerator_factors)
+  time_numerator_factors :: [(Time_numerator_factor, Int)]
+  time_numerator_factors = [(Two, 2), (Three, 3)]
+  -- | Convert time numerator to int. Mostly for internal use. ---
+  time_numerator_to_int :: [Time_numerator_factor] -> Int ---
+  time_numerator_to_int num = product (time_numerator_factor_to_int <$> num) ---
+  -- | Remove the topmost division from time signature. For example, 4/4 gets transformed into 2/4. 1/4 gets transformed into
+  -- 1/8. Mostly for internal use.
+  time_subdivision :: Time -> Either Error Time
+  time_subdivision (Time num den) =
+    case num of
+      [] ->
+        case next_basic_length den of
+          Nothing -> Left (Is_out_of_range_without_location Note_length_denominator_IOOR)
+          Just den' -> Right (Time [] den')
+      _ : num' -> Right (Time num' den)
diff --git a/Composition/Write.hs b/Composition/Write.hs
new file mode 100644
--- /dev/null
+++ b/Composition/Write.hs
@@ -0,0 +1,137 @@
+{-| ---
+Description: Write the custom file format for scores. ---
+ ---
+* Write the custom file format for scores. ---
+-} ---
+module Composition.Write (write) where ---
+  import Composition.Errors ---
+  import Composition.Notes ---
+  import Composition.Score ---
+  import Composition.Time ---
+  import Control.Monad.Except ---
+  import Data.List ---
+  import Data.Map ---
+  import Data.Set as Set ---
+  import Parser.Files ---
+  import Parser.Utilities ---
+  -- | Encode the score in .aoi format and write it to the specified file. ---
+  write :: File_path -> Score -> ExceptT Error IO () ---
+  write file_path score = write_file "aoi" writeFile File_error file_path (write_score score) ---
+  write_accidental :: Accidental -> String ---
+  write_accidental accidental = ---
+    case accidental of ---
+      Flat -> "b" ---
+      Natural -> "" ---
+      Sharp -> "#" ---
+  write_basic_length :: Basic_length -> String ---
+  write_basic_length len = show (basic_length_denominator len) ---
+  write_brackets :: String -> String -> String -> String ---
+  write_brackets left_bracket right_bracket x = left_bracket <> x <> right_bracket ---
+  write_clef :: Clef note_type -> String ---
+  write_clef clef = ---
+    case clef of ---
+      Pitched_clef {clef_name, transposition} -> ---
+        write_struct "Pitched_clef" [write_eq "clef_name" (show clef_name), write_eq "transposition" (show transposition)] ---
+      Percussion_clef -> "Percussion_clef" ---
+  write_clef_and_stave :: Clef_and_stave note_type -> String ---
+  write_clef_and_stave (Clef_and_stave {clef, stave}) = ---
+    write_struct "Clef_and_stave" [write_eq "clef" (write_clef clef), write_eq "stave" (write_stave stave)] ---
+  write_curly_brackets :: String -> String ---
+  write_curly_brackets = write_brackets "{" "}" ---
+  write_dot :: Dot -> Char ---
+  write_dot Dot = '.' ---
+  write_eq :: String -> String -> String ---
+  write_eq x y = x <-> "=" <-> y ---
+  write_event :: Event note_type -> String ---
+  write_event event = ---
+    case event of ---
+      Event notes len -> write_notes notes <> write_length len ---
+      Triplet events -> write_events events ---
+  write_events :: [Event note_type] -> String ---
+  write_events = write_list write_event ---
+  write_header :: Map Header_field String -> String ---
+  write_header header = write_list write_header_field (assocs header) ---
+  write_header_field :: (Header_field, String) -> String ---
+  write_header_field (name, value) = write_eq (show name) (write_quotes value) ---
+  write_initial_position :: Initial_position -> String ---
+  write_initial_position (Initial_position num den) = ---
+    case num of ---
+      0 -> "0" ---
+      _ -> show num <> "/" <> write_basic_length den ---
+  write_instrument_clefs_and_staves :: Instrument_clefs_and_staves note_type -> String ---
+  write_instrument_clefs_and_staves ---
+    (Instrument_clefs_and_staves {instrument_name, midi_instrument, velocity, clefs_and_staves}) = ---
+      write_struct ---
+        "Instrument_clefs_and_staves" ---
+        [ ---
+          write_eq "instrument_name" (write_quotes instrument_name), ---
+          write_eq "midi_instrument" (show midi_instrument), ---
+          write_eq "velocity" (show velocity), ---
+          write_eq "clefs_and_staves" (write_list write_clef_and_stave clefs_and_staves)] ---
+  write_key :: Set Note_name -> String ---
+  write_key key = write_list write_note_name (Set.elems key) ---
+  write_length :: Length -> String ---
+  write_length (Length len dots) = write_basic_length len <> (write_dot <$> dots) ---
+  write_list :: (t -> String) -> [t] -> String ---
+  write_list write_t x = write_square_brackets (intercalate " " (write_t <$> x)) ---
+  write_natural_note_name :: Natural_note_name -> String ---
+  write_natural_note_name natural_note_name = ---
+    case natural_note_name of ---
+      C_natural -> "C" ---
+      D_natural -> "D" ---
+      E_natural -> "E" ---
+      F_natural -> "F" ---
+      G_natural -> "G" ---
+      A_natural -> "A" ---
+      B_natural -> "B" ---
+  write_note :: Note note_type -> String ---
+  write_note note = ---
+    case note of ---
+      Pitched_note octave note_name -> write_note_name note_name <> show octave ---
+      Unpitched_note -> "x" ---
+  write_note_name :: Note_name -> String ---
+  write_note_name note_name = ---
+    let ---
+      (natural_note_name, accidental) = deconstruct_note_name note_name in ---
+      write_natural_note_name natural_note_name <> write_accidental accidental ---
+  write_notes :: Notes note_type -> String ---
+  write_notes maybe_notes = ---
+    case maybe_notes of ---
+      Notes notes -> write_list write_note (Set.elems notes) ---
+      Tie -> "+" ---
+  write_part :: Part -> String ---
+  write_part (Part {title, key, time, initial_position, tempo, stave_groups}) = ---
+    write_struct ---
+      "Part" ---
+      [ ---
+        write_eq "title" (write_quotes title), ---
+        write_eq "key" (write_key key), ---
+        write_eq "time" (write_time time), ---
+        write_eq "initial_position" (write_initial_position initial_position), ---
+        write_eq "tempo" (show tempo), ---
+        write_eq ---
+          "stave_groups" ---
+          (write_list (write_list (write_pitched_or_unpitched write_instrument_clefs_and_staves)) stave_groups)] ---
+  write_pitched_or_unpitched :: (forall note_type . f note_type -> String) -> Pitched_or_unpitched f -> String ---
+  write_pitched_or_unpitched write_f x = ---
+    pitched_or_unpitched (\ _ -> "Pitched") (\ _ -> "Unpitched") x <-> pitched_and_unpitched write_f x ---
+  write_quotes :: String -> String ---
+  write_quotes = write_brackets "\"" "\"" ---
+  write_score :: Score -> String ---
+  write_score (Score {title, header, parts}) = ---
+    write_struct ---
+      "Score" ---
+      [ ---
+        write_eq "title" (write_quotes title), ---
+        write_eq "header" (write_header header), ---
+        write_eq "parts" (write_list write_part parts)] ---
+  write_stave :: Stave note_type -> String ---
+  write_stave stave = write_list write_events (stave_to_events stave) ---
+  write_square_brackets :: String -> String ---
+  write_square_brackets = write_brackets "[" "]" ---
+  write_struct :: String -> [String] -> String ---
+  write_struct name fields = name <-> write_curly_brackets (intercalate " " fields) ---
+  write_time :: Time -> String ---
+  write_time (Time num den) = "Time" <-> write_list write_time_numerator_factor num <-> write_basic_length den ---
+  write_time_numerator_factor :: Time_numerator_factor -> String ---
+  write_time_numerator_factor time_numerator_factor = show (time_numerator_factor_to_int time_numerator_factor) ---
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,30 +1,30 @@
-Copyright (c) 2020, Liisi Kerik
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-
-    * Redistributions in binary form must reproduce the above
-      copyright notice, this list of conditions and the following
-      disclaimer in the documentation and/or other materials provided
-      with the distribution.
-
-    * Neither the name of Liisi Kerik nor the names of other
-      contributors may be used to endorse or promote products derived
-      from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2020, Liisi Kerik
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Liisi Kerik nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Main.hs b/Main.hs
new file mode 100644
--- /dev/null
+++ b/Main.hs
@@ -0,0 +1,267 @@
+module Main (main) where --
+  import Composition.Errors --
+  import Composition.Lilypond --
+  import Composition.MIDI --
+  import Composition.Keyboard --
+  import Composition.Parser --
+  import Composition.Score --
+  import Composition.Write --
+  import Control.Monad.Except --
+  import Control.Monad.Trans.Except --
+  import Data.Char --
+  import Data.Functor --
+  import Data.List --
+  import Data.Maybe --
+  import Parser.Files --
+  import Parser.Locations --
+  import Parser.Parser --
+  import Parser.Utilities --
+  import System.Environment --
+  data Char_class = --
+    Delimiter_char Token | --
+    File_path_char Char | --
+    Invalid_char | --
+    Keyword_char Char | --
+    Nonzero_char Char | --
+    Quote_char | --
+    Text_char Char | --
+    Whitespace_char | --
+    Zero_char --
+  data Command = --
+    Keyboard_command { --
+      playable :: File_path, --
+      source :: File_path, --
+      header_instrument :: Maybe String, --
+      midi_instrument :: MIDI_instrument, --
+      destination :: File_path} | --
+    Lilypond {source :: File_path, destination :: File_path} | --
+    MIDI {source :: File_path, destination :: File_path} --
+  type Parser = Parser' Token Error --
+  data Token = --
+    Destination_token | --
+    Eq_token | --
+    File_path_token String | --
+    Instrument_name_token | --
+    Keyboard_token | --
+    Left_curly_bracket_token | --
+    Lilypond_token | --
+    MIDI_instrument_token | --
+    MIDI_token | --
+    Nat_token Integer | --
+    Playable_token | --
+    Right_curly_bracket_token | --
+    Source_token | --
+    Text_token String --
+  type Tokeniser = Tokeniser' Char_class Token Error --
+  deriving instance Eq Char_class --
+  deriving instance Eq Token --
+  deriving instance Show Char_class --
+  deriving instance Show Command --
+  deriving instance Show Token --
+  classify_char :: Char -> Char_class --
+  classify_char c = --
+    case c of --
+      ' ' -> Whitespace_char --
+      _ | elem c "!#$%&'()*+-,:;<>?@[]^`|~" -> Text_char c --
+      '"' -> Quote_char --
+      _ | elem c "'_" || isLetter c -> Keyword_char c --
+      _ | elem c "./" -> File_path_char c --
+      '0' -> Zero_char --
+      _ | isDigit c && c /= '0' -> Nonzero_char c --
+      '=' -> Delimiter_char Eq_token --
+      '{' -> Delimiter_char Left_curly_bracket_token --
+      '}' -> Delimiter_char Right_curly_bracket_token --
+      _ -> Invalid_char --
+  construct_keyword :: String -> Either (Location -> Error) Token --
+  construct_keyword keyword = --
+    case lookup keyword keywords of --
+      Nothing -> Left (\ _ -> Invalid_keyword keyword) --
+      Just token -> Right token --
+  convert_midi_instrument :: Integer -> Either (Location -> Error) MIDI_instrument --
+  convert_midi_instrument midi_instrument = --
+    do --
+      check (Is_out_of_range_with_location MIDI_instrument_code_IOOR) (between 0 127 midi_instrument) --
+      Right (fromIntegral midi_instrument) --
+  delimiter_char :: Char_class -> Maybe Token --
+  delimiter_char char_class = --
+    case char_class of --
+      Delimiter_char token -> Just token --
+      _ -> Nothing --
+  file_path_char :: Char_class -> Maybe Char --
+  file_path_char char_class = --
+    case char_class of --
+      File_path_char c -> Just c --
+      Keyword_char c -> Just c --
+      Nonzero_char c -> Just c --
+      Zero_char -> Just '0' --
+      _ -> Nothing --
+  file_path_token :: Token -> Maybe String --
+  file_path_token token = --
+    case token of --
+      File_path_token file_path -> Just file_path --
+      _ -> Nothing --
+  keyword_char :: Char_class -> Maybe Char --
+  keyword_char char_class = --
+    case char_class of --
+      Keyword_char c -> Just c --
+      _ -> Nothing --
+  keywords :: [(String, Token)] --
+  keywords = --
+    [ --
+      ("Keyboard", Keyboard_token), --
+      ("Lilypond", Lilypond_token), --
+      ("MIDI", MIDI_token), --
+      ("destination", Destination_token), --
+      ("instrument_name", Instrument_name_token), --
+      ("midi_instrument", MIDI_instrument_token), --
+      ("playable", Playable_token), --
+      ("source", Source_token)] --
+  main :: IO () --
+  main = --
+    do --
+      args <- getArgs --
+      result <- runExceptT (main' (intercalate " " args)) --
+      case result of --
+        Left err -> putStrLn (write_error err) --
+        Right () -> return () --
+  main' :: String -> ExceptT Error IO () --
+  main' text = --
+    do --
+      command <- except (Main.parse text) --
+      case command of --
+        Keyboard_command {playable = playable_file, source, header_instrument, midi_instrument, destination} -> --
+          do --
+            playable <- parse_playable playable_file --
+            original_score <- Composition.Parser.parse source --
+            keyboard_score <- except (keyboard playable header_instrument midi_instrument original_score) --
+            write destination keyboard_score --
+        Lilypond {source, destination} -> --
+          do --
+            score <- Composition.Parser.parse source --
+            lilypond destination score --
+        MIDI {source, destination} -> --
+          do --
+            score <- Composition.Parser.parse source --
+            midi score destination --
+      return () --
+  nat_char :: Char_class -> Maybe Char --
+  nat_char char_class = --
+    case char_class of --
+      Zero_char -> Just '0' --
+      Nonzero_char c -> Just c --
+      _ -> Nothing --
+  nat_token :: Token -> Maybe Integer --
+  nat_token token = --
+    case token of --
+      Nat_token i -> Just i --
+      _ -> Nothing --
+  nonzero_char :: Char_class -> Maybe Char --
+  nonzero_char char_class = --
+    case char_class of --
+      Nonzero_char c -> Just c --
+      _ -> Nothing --
+  parse :: String -> Either Error Command --
+  parse command = fromJust (parse' classify_char (return next_char) tokenise parse_command Parse_error command) --
+  parse_command :: Parser Command --
+  parse_command = parse_keyboard <+> parse_lilypond <+> parse_midi --
+  parse_curly_brackets :: Parser t -> Parser t --
+  parse_curly_brackets = parse_brackets Left_curly_bracket_token Right_curly_bracket_token --
+  parse_eq :: Parser () --
+  parse_eq = parse_token Eq_token --
+  parse_field :: Token -> Parser t -> Parser t --
+  parse_field name parse_t = --
+    do --
+      parse_token name --
+      parse_eq --
+      parse_t --
+  parse_file_path :: String -> Parser File_path --
+  parse_file_path ext = fmap_filter_parser (parse_file_path' (\ err _ -> File_error err) ext) (parse_token' file_path_token) --
+  parse_keyboard :: Parser Command --
+  parse_keyboard = --
+    parse_struct --
+      Keyboard_token --
+      (do --
+        playable <- parse_field Playable_token (parse_file_path "key") --
+        source <- parse_field Source_token (parse_file_path "aoi") --
+        header_instrument <- parse_optional_field Instrument_name_token parse_text --
+        midi_instrument <- parse_field MIDI_instrument_token parse_midi_instrument --
+        destination <- parse_field Destination_token (parse_file_path "aoi") --
+        return (Keyboard_command {playable, source, header_instrument, midi_instrument, destination})) --
+  parse_lilypond :: Parser Command --
+  parse_lilypond = --
+    parse_struct --
+      Lilypond_token --
+      (do --
+        source <- parse_field Source_token (parse_file_path "aoi") --
+        destination <- parse_field Destination_token (parse_file_path "ly") --
+        return (Lilypond {source, destination})) --
+  parse_midi :: Parser Command --
+  parse_midi = --
+    parse_struct --
+      MIDI_token --
+      (do --
+        source <- parse_field Source_token (parse_file_path "aoi") --
+        destination <- parse_field Destination_token (parse_file_path "mid") --
+        return (MIDI {source, destination})) --
+  parse_midi_instrument :: Parser MIDI_instrument --
+  parse_midi_instrument = fmap_filter_parser convert_midi_instrument parse_nat --
+  parse_nat :: Parser Integer --
+  parse_nat = parse_token' nat_token --
+  parse_optional_field :: Token -> Parser t -> Parser (Maybe t) --
+  parse_optional_field name parse_t = parse_default Nothing (Just <$> parse_field name parse_t) --
+  parse_struct :: Token -> Parser Command -> Parser Command --
+  parse_struct name parse_fields' = --
+    do --
+      parse_token name --
+      parse_curly_brackets parse_fields' --
+  parse_text :: Parser String --
+  parse_text = parse_token' text_token --
+  text_char :: Char_class -> Maybe Char --
+  text_char char_class = --
+    case char_class of --
+      Delimiter_char token -> --
+        Just --
+          (case token of --
+            Eq_token -> '=' --
+            Left_curly_bracket_token -> '{' --
+            Right_curly_bracket_token -> '}' --
+            _ -> undefined) --
+      File_path_char c -> Just c --
+      Invalid_char -> Nothing --
+      Keyword_char c -> Just c --
+      Nonzero_char c -> Just c --
+      Quote_char -> Nothing --
+      Text_char c -> Just c --
+      Whitespace_char -> Just ' ' --
+      Zero_char -> Just '0' --
+  text_token :: Token -> Maybe String --
+  text_token token = --
+    case token of --
+      Text_token text -> Just text --
+      _ -> Nothing --
+  tokenise :: Tokeniser () --
+  tokenise = void (parse_many tokenise_1) --
+  tokenise_1 :: Tokeniser () --
+  tokenise_1 = --
+    tokenise_delimiter <+> ((tokenise_nat <+> tokenise_keyword) <+ tokenise_file_path) <+> tokenise_text <+> tokenise_whitespace --
+  tokenise_delimiter :: Tokeniser () --
+  tokenise_delimiter = add_token (parse_token' delimiter_char) --
+  tokenise_file_path :: Tokeniser () --
+  tokenise_file_path = add_token (File_path_token <$> parse_some (parse_token' file_path_char)) --
+  tokenise_keyword :: Tokeniser () --
+  tokenise_keyword = add_token (fmap_filter_parser construct_keyword (parse_some (parse_token' keyword_char))) --
+  tokenise_nat :: Tokeniser () --
+  tokenise_nat = add_token (Nat_token <$> (tokenise_zero <+> tokenise_positive_int)) --
+  tokenise_positive_int :: Tokeniser Integer --
+  tokenise_positive_int = read <$> ((:) <$> parse_token' nonzero_char <*> parse_many (parse_token' nat_char)) --
+  tokenise_quotes :: Tokeniser t -> Tokeniser t --
+  tokenise_quotes = parse_brackets Quote_char Quote_char --
+  tokenise_text :: Tokeniser () --
+  tokenise_text = add_token (Text_token <$> tokenise_quotes (parse_many (parse_token' text_char))) --
+  tokenise_whitespace :: Tokeniser () --
+  tokenise_whitespace = parse_token Whitespace_char --
+  tokenise_zero :: Tokeniser Integer --
+  tokenise_zero = --
+    do --
+      parse_token Zero_char --
+      return 0 --
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
