packages feed

Aoide 1.0.0.0 → 2.0.0.0

raw patch · 16 files changed

+2582/−2153 lines, 16 files

Files

Aoide.cabal view
@@ -3,15 +3,16 @@ 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.+  addition it contains a module with some music-theoretical functions and generates keyboard reductions. The library is focused+  on the very basics of Western musical notation and does not support dynamic indications, articulation marks, double+  accidentals, tempo changes, polyrhythms, time signature changes and microtonality.+extra-doc-files: Keyboard.key, README.md, Scores/**/*.aoi 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+version: 2.0.0.0 common Common   build-depends:     Kawaii-Parser, barbies, base < 4.22, bytestring, containers, directory, filepath, generic-lens, lens, mtl, process, transformers@@ -41,11 +42,11 @@     Composition.Keyboard,     Composition.Lilypond,     Composition.MIDI,+    Composition.Notation,     Composition.Notes,     Composition.Parser,     Composition.Score,     Composition.Theory,-    Composition.Time,     Composition.Write executable Aoide   import: Common@@ -55,11 +56,11 @@     Composition.Keyboard,     Composition.Lilypond,     Composition.MIDI,+    Composition.Notation,     Composition.Notes,     Composition.Parser,     Composition.Score,     Composition.Theory,-    Composition.Time,     Composition.Write source-repository head   location: https://github.com/liisikerik/aoide.git
Composition/Errors.hs view
@@ -1,42 +1,44 @@ {-|-Description: Errors.+Errors. -}-module Composition.Errors (Error (..), Is_out_of_range_type (..), write_error) where+module Composition.Errors (Error (..), Is_out_of_range_type (..)) where+  import Composition.Notes   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 | ---+    An_event_crosses_the_bar_line |+    Conflicting_accidentals_in_key_signature |+    Duplicate_header_fields (Maybe Location) |+    Duplicate_keys Location |+    Duplicate_note_names_in_key_signature Location |+    Duplicate_notes Location |+    Empty_triplet |+    Event_or_part_length_is_out_of_range_for_MIDI |+    File_error File_error |+    Initial_position_is_out_of_range |+    Invalid_character_in_text |+    Invalid_note_length Int Location |+    Invalid_note_name Natural_note_name Accidental Location |+    Invalid_time_numerator_factor Int Location |+    Invalid_word String (Maybe 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.+    Key_is_out_of_range Int Location |+    MIDI_instrument_code_is_out_of_range_command Int |+    MIDI_instrument_code_is_out_of_range_data Int |+    MIDI_instrument_code_is_out_of_range_score Int Location |+    Non_positive_note_length |+    Note_is_out_of_range_for_MIDI (Note Pitched) |+    Note_length_denominator_contains_factors_other_than_2_and_3 |+    Parse_error_command |+    Parse_error_playables Location |+    Parse_error_score Location |+    Track_ends_with_an_incomplete_triplet |+    Track_length_mismatch |+    Velocity_out_of_range Location   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 | ---+    Note_length_denominator_IOOR |     Tempo_IOOR |     The_least_common_denominator_of_note_lengths_IOOR |     The_number_of_pitched_tracks_IOOR |@@ -45,51 +47,3 @@     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"
Composition/Keyboard.hs view
@@ -1,710 +1,373 @@-{-| ----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 -----} ---+{-|+Keyboard playability and reductions.+-}+module Composition.Keyboard (+  Keyboard (..),+  Playable (..),+  keyboard_playable,+  notes_to_keyboard,+  parse_playables,+  reduction) where+  import Composition.Errors+  import Composition.Notation+  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 score part.+  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 with the right hand only starting from D#/Eb and A#/Bb.+  data Playable = Playable {semitones :: [Semitones], 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+  all_notes :: [Playable] -> [Note Pitched] -> [Set (Note Pitched)]+  all_notes playables notes =+    Set.empty : all_notes' Set.empty notes where+    all_notes' :: Set (Note Pitched) -> [Note Pitched] -> [Set (Note Pitched)]+    all_notes' picked_notes notes' =+      case notes' of+        [] -> []+        note : notes'' ->+          let+            picked_notes' = Set.insert note picked_notes in+            case notes_playable playables picked_notes' of+              False -> all_notes' picked_notes notes''+              True -> picked_notes' : all_notes' picked_notes' notes''+  best :: (Eq u) => (t -> u) -> ([u] -> u) -> [t] -> [t]+  best metric best_metric x = List.filter (\ y -> best_metric (metric <$> x) == metric y) x+  best_keyboard :: [Keyboard (Set (Note Pitched))] -> Keyboard (Set (Note Pitched))+  best_keyboard =+    (+      head <$>+      best right_hand_note_count maximum <$>+      best narrower_hand_span minimum <$>+      best wider_hand_note_count minimum <$>+      best wider_hand_span minimum <$>+      best crosses_middle_c minimum <$>+      best note_count maximum)+  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'+  continuing_notes_and_event ::+    Set (Note Pitched) -> Length_fraction -> Set (Note Pitched) -> Set (Note Pitched) -> (Set (Note Pitched), Event' Notes_out)+  continuing_notes_and_event ties len old_notes new_notes =+    let+      (continuing_notes, notes_out) =+        case Set.elems new_notes of+          [] ->+            case Set.elems (Set.union old_notes ties) of+              [] -> (Set.empty, Rest_out)+              _ : _ -> (old_notes, Tie_out)+          _ : _ -> (new_notes, Notes_out new_notes) in+      (continuing_notes, Event' notes_out len)+  crosses_middle_c :: Keyboard (Set (Note Pitched)) -> Int+  crosses_middle_c (Keyboard left_hand_notes right_hand_notes) =+    crosses_middle_c' id Set.findMax left_hand_notes `max` crosses_middle_c' flip Set.findMin right_hand_notes+  crosses_middle_c' ::+    (+      ((Note Pitched -> Note Pitched -> Int) -> Note Pitched -> Note Pitched -> Int) ->+      (Set (Note Pitched) -> Note Pitched) ->+      Set (Note Pitched) ->+      Int)+  crosses_middle_c' order_arguments innermost_note notes =+    order_arguments distance_in_semitones (Pitched_note 4 C) (innermost_note (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_out_to_events :: [Event' Notes_out] -> [Event_fraction Pitched]+  events_out_to_events events =+    case events of+      [] -> []+      Event' notes len : events' -> events_out_to_events' (Event' (notes_out_to_set notes) len) events'+  events_out_to_events' :: Event_fraction Pitched -> [Event' Notes_out] -> [Event_fraction Pitched]+  events_out_to_events' event@(Event' notes_0 len_0) events =+    case events of+      [] -> [event]+      Event' maybe_notes_1 len_1 : events' ->+        case maybe_notes_1 of+          Rest_out ->+            case Set.elems notes_0 of+              [] -> events_out_to_events' (Event' Set.empty (len_0 + len_1)) events'+              _ : _ -> event : events_out_to_events' (Event' Set.empty len_1) events'+          Notes_out notes_1 -> event : events_out_to_events' (Event' notes_1 len_1) events'+          Tie_out -> events_out_to_events' (Event' notes_0 (len_0 + len_1)) events'+  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 union_notes_in (Notes_in Set.empty Set.empty) (fst <$> heads_and_tails)) len :+            events_to_events_in' (snd <$> heads_and_tails))+  hand_span :: Set (Note Pitched) -> Int+  hand_span notes =+    case Set.elems notes of+      [] -> -1+      _ -> distance_in_semitones (minimum notes) (maximum notes)+  hands_not_crossed :: Keyboard (Set (Note Pitched)) -> Bool+  hands_not_crossed (Keyboard left_hand_notes right_hand_notes) =+    case (Set.elems left_hand_notes, Set.elems right_hand_notes) of+      (_ : _, _ : _) -> Set.findMax left_hand_notes < Set.findMin right_hand_notes+      _ -> True+  head_and_tail :: Length_fraction -> [Event' (Notes_in')] -> (Notes_in, [Event' (Notes_in')])+  head_and_tail len events =+    case events of+      [] -> undefined+      Event' notes len' : events' ->+        (+          transform_notes_in notes,+          case () of+            () | len == len' -> events'+            () -> Event' (notes_in_to_tie notes) (len' - len) : events')+  -- | Check if this configuration is playable on keyboard.+  keyboard_playable :: [Playable] -> Keyboard (Set (Note Pitched)) -> Bool+  keyboard_playable playables keyboard@(Keyboard left_hand_notes right_hand_notes) =+    (+      notes_playable (mirror_playables playables) left_hand_notes &&+      hands_not_crossed keyboard &&+      notes_playable playables right_hand_notes)+  mirror_key :: Int -> Int+  mirror_key key = mod (4 - key) 12+  mirror_playable :: Playable -> Playable+  mirror_playable (Playable semitones keys) = Playable (reverse semitones) (Set.fromList (mirror_key <$> Set.elems keys))+  mirror_playables :: [Playable] -> [Playable]+  mirror_playables = (<$>) mirror_playable+  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 keyboard = minimum (hand_span <$> keyboard)+  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+  note_count :: Keyboard (Set (Note Pitched)) -> Int+  note_count keyboard = sum (Set.size <$> keyboard)+  notes_in_to_tie :: Notes_in' -> Notes_in'+  notes_in_to_tie maybe_notes =+    case maybe_notes of+      Notes_in' notes -> Tie_in' notes+      Tie_in' notes -> Tie_in' notes+  notes_out_to_set :: Notes_out -> Set (Note Pitched)+  notes_out_to_set maybe_notes =+    case maybe_notes of+      Rest_out -> Set.empty+      Notes_out notes -> notes+      Tie_out -> Set.empty+  notes_playable :: [Playable] -> Set (Note Pitched) -> Bool+  notes_playable playables notes =+    case Set.elems notes of+      [] -> True+      note@(Pitched_note _ note_name) : notes' ->+        any (notes_playable' (notes_to_semitones note notes') (semitones_from_c note_name)) playables+  notes_playable' :: [Int] -> Int -> Playable -> Bool+  notes_playable' semitones' starting_key (Playable semitones starting_keys) =+    semitones == semitones' && any ((==) starting_key) starting_keys+  notes_to_semitones :: Note Pitched -> [Note Pitched] -> [Int]+  notes_to_semitones note_0 notes =+    case notes of+      [] -> []+      note_1 : notes' -> distance_in_semitones note_0 note_1 : notes_to_semitones note_1 notes'+  -- | Check if the set of notes is playable on keyboard and distribute the notes between hands as well as possible.+  notes_to_keyboard :: [Playable] -> Set (Note Pitched) -> Maybe (Keyboard (Set (Note Pitched)))+  notes_to_keyboard playables notes =+    case List.filter (keyboard_playable playables) (split_notes_between_hands notes) of+      [] -> Nothing+      keyboards -> Just (best_keyboard keyboards)+  parse_key :: Parser Int+  parse_key = filter_parser ((>=) 11) Key_is_out_of_range 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 :: Parser Playable+  parse_playable = Playable <$> parse_list' parse_positive_int <*> parse_keys+  -- | Parse keyboard playability file.+  parse_playables :: File_path -> ExceptT Error IO [Playable]+  parse_playables file_path =+    do+      playables <- read_file "key" readFile File_error file_path+      except (fromJust (parse' classify_char next_location tokenise parse_playables' Parse_error_playables playables))+  parse_playables' :: Parser [Playable]+  parse_playables' = parse_list Newline_token parse_playable+  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+  pitched_events :: Pitched_or_unpitched Track -> Maybe [Event_fraction Pitched]+  pitched_events = pitched_or_unpitched (\ (Track {events}) -> Just events) (\ _ -> Nothing)+  positive_int_token :: Token -> Maybe Int+  positive_int_token token =+    case token of+      Positive_int_token i -> Just i+      _ -> Nothing+  -- | Keyboard reduction. You have to provide playability data, optional instrument score header field and a MIDI instrument+  -- code.+  reduction :: [Playable] -> Map Header_field String -> MIDI_instrument -> Score -> Either Error Score+  reduction playables header midi_instrument (Score {parts}) =+    do+      keyboard_parts <- traverse keyboard_part parts+      Right (Score {header, parts = keyboard_parts}) where+    all_keyboards :: Set (Note Pitched) -> [Keyboard (Set (Note Pitched))]+    all_keyboards notes =+      List.filter+        hands_not_crossed+        (+          Keyboard <$>+          all_notes (mirror_playables playables) (Set.elems notes) <*>+          all_notes playables (reverse (Set.elems notes)))+    events_to_keyboard :: Keyboard (Set (Note Pitched)) -> [Event' Notes_in] -> Keyboard [Event' Notes_out]+    events_to_keyboard old_keyboard events =+      case events of+        [] -> pure []+        Event' (Notes_in new_notes ties) len : events' ->+          let+            new_keyboard = best_keyboard (all_keyboards new_notes)+            continuing_notes_and_event_keyboard = continuing_notes_and_event ties len <$> old_keyboard <*> new_keyboard in+            (+              (:) <$>+              (snd <$> continuing_notes_and_event_keyboard) <*>+              events_to_keyboard (fst <$> continuing_notes_and_event_keyboard) events')+    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+            (add_notational_information time initial_position)+            (+              events_out_to_events <$>+              events_to_keyboard (pure Set.empty) (events_to_events_in (Maybe.mapMaybe pitched_events tracks)))+        Right+          (Part {+            title = part_title,+            key,+            time,+            initial_position,+            tempo,+            stave_groups =+              [+                [+                  Pitched+                    (Instrument_and_staves {+                      instrument_name = "",+                      short_instrument_name = "",+                      midi_instrument,+                      velocity = max_velocity,+                      staves =+                        [+                          Stave {+                            clef = Pitched_clef {clef_name = Treble, transposition = 0},+                            tracks = One_track right_hand_track},+                          Stave {+                            clef = Pitched_clef {clef_name = Bass, transposition = 0},+                            tracks = One_track left_hand_track}]})]]})+  right_hand_note_count :: Keyboard (Set (Note Pitched)) -> Int+  right_hand_note_count (Keyboard _ right_hand_notes) = Set.size right_hand_notes+  split_notes_between_hands :: Set (Note Pitched) -> [Keyboard (Set (Note Pitched))]+  split_notes_between_hands notes = Keyboard Set.empty notes : split_notes_between_hands' Set.empty (Set.elems notes)+  split_notes_between_hands' :: Set (Note Pitched) -> [Note Pitched] -> [Keyboard (Set (Note Pitched))]+  split_notes_between_hands' left_hand_notes notes =+    case notes of+      [] -> []+      note : notes' ->+        let+          left_hand_notes' = Set.insert note left_hand_notes in+          Keyboard left_hand_notes' (Set.fromList notes') : split_notes_between_hands' left_hand_notes' 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+  transform_notes_in :: Notes_in' -> Notes_in+  transform_notes_in maybe_notes =+    case maybe_notes of+      Notes_in' notes -> Notes_in notes Set.empty+      Tie_in' notes -> Notes_in Set.empty notes+  union_notes_in :: Notes_in -> Notes_in -> Notes_in+  union_notes_in (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)+  wider_hand_span :: Keyboard (Set (Note Pitched)) -> Int+  wider_hand_span keyboard = maximum (hand_span <$> keyboard)+  wider_hand_note_count :: Keyboard (Set (Note Pitched)) -> Int+  wider_hand_note_count keyboard = Set.size (maximumBy (comparing hand_span) keyboard)
Composition/Lilypond.hs view
@@ -1,394 +1,380 @@-{-| ----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) ---+{-|+Generates Lilypond files.+-}+module Composition.Lilypond (lilypond) where+  import Composition.Errors+  import Composition.Notation+  import Composition.Notes+  import Composition.Score+  import Composition.Theory+  import Control.Lens.Combinators hiding (index, parts)+  import Control.Lens.Operators+  import Control.Monad.Except+  import Control.Monad.State.Strict hiding (State)+  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 State+  deriving instance Show Header_field'+  deriving instance Show State+  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 value where+    write_header_field_name :: String+    write_header_field_name =+      case name of+        Left_text -> "poet"+        Overtitle -> "dedication"+        Right_text -> "composer"+        Subtitle -> "subtitle"+        Subsubtitle -> "subsubtitle"+        Title -> "title"+  -- | Encodes the score in Lilypond format and writes it to the specified file.+  lilypond :: Score -> File_path -> ExceptT Error IO ()+  lilypond score file_path =+    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 (basic_length_denominator len)+  write_brackets :: String -> String -> String -> String+  write_brackets left_bracket right_bracket x = left_bracket <> x <> right_bracket+  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_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_header :: [Header_field'] -> Either Error String+  write_header header =+    do+      fields <- traverse write_header_field header+      Right ("\\header" <-> write_curly_brackets (intercalate " " fields))+  write_header_field :: Header_field' -> Either Error String+  write_header_field (Header_field' name value) = write_eq name <$> write_quotes value+  write_key_accidental :: Accidental -> String+  write_key_accidental accidental = toUpper <$> show accidental+  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+      Just accidentals' ->+        case accidentals' of+          [accidental] -> write_key_note_name' accidental+          _ -> Left Conflicting_accidentals_in_key_signature) where+    write_key_natural_note_name :: String+    write_key_natural_note_name = show (fromEnum natural_note_name)+    write_key_note_name' :: Accidental -> Either Error String+    write_key_note_name' accidental =+      Right (write_round_brackets (write_key_natural_note_name <-> ". ," <> write_key_accidental accidental))+  write_language :: Either Error String+  write_language =+    do+      language <- write_quotes "english.ly"+      Right ("\\include" <-> 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 = [toLower (head (show natural_note_name))]+  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 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+          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 name value = "#" <> write_round_brackets ("ly:set-option '" <> name <-> value)+  write_options :: String+  write_options = 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+      header <- write_header [Header_field' "piece" title]+      set_key <- write_set_key+      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+        (+          set_key <->+          time_and_initial_position <->+          "\\score" <->+          write_curly_brackets (header <-> write_angular_brackets_2 (intercalate " " written_stave_groups))) where+    write_event :: Int -> Event Pitched -> StateT State (Either Error) (Bool, String)+    write_event triplets event =+      case event of+        Event notes len ->+          do+            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_events <- write_events' (1 + triplets) events+            case maybe_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+        initial_position' <- initial_position_to_fraction time initial_position+        maybe_events <- evalStateT (write_events' 0 events) (State {state_notes = False, state_position = initial_position'})+        Right+          (case maybe_events of+            Nothing -> ""+            Just (_, written_events) -> written_events)+    write_events' :: Int -> [Event Pitched] -> StateT 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_events <- write_events' triplets events'+            return+              (Just+                (+                  tie_0,+                  intercalate+                    " "+                    (+                      written_event :+                      case maybe_events of+                        Nothing -> []+                        Just (tie_1, written_events) ->+                          (+                            (case tie_1 of+                              False -> []+                              True -> ["~"]) <>+                            [written_events]))))+    write_initial_position :: Either Error (Maybe String)+    write_initial_position =+      do+        initial_position' <- initial_position_to_fraction time initial_position+        let num :% den = measure_length time - initial_position'+        Right+          (case initial_position' of+            0 -> Nothing+            _ -> Just (show den <-> "*" <-> show num))+    write_instrument_and_staves :: Instrument_and_staves note_type -> Either Error String+    write_instrument_and_staves (Instrument_and_staves {instrument_name, short_instrument_name, staves}) =+      do+        set_instrument_name <- write_set_instrument_name+        written_staves <- traverse write_stave staves+        Right+          (+            "\\new PianoStaff \\with" <->+            write_curly_brackets set_instrument_name <->+            write_angular_brackets_2 (intercalate " " written_staves)) where+      write_set_instrument_name :: Either Error String+      write_set_instrument_name =+        do+          written_instrument_name <- write_quotes instrument_name+          written_short_instrument_name <- write_quotes short_instrument_name+          Right+            (write_eq "instrumentName" written_instrument_name <-> write_eq "shortInstrumentName" written_short_instrument_name)+    write_key :: Either Error String+    write_key =+      do+        written_key <-+          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 " " written_key))+    write_set_initial_position :: Either Error [String]+    write_set_initial_position =+      do+        maybe_written_initial_position <- write_initial_position+        Right+          (case maybe_written_initial_position of+            Nothing -> []+            Just written_initial_position -> ["\\partial" <-> written_initial_position])+    write_set_key :: Either Error String+    write_set_key =+      do+        written_key <- write_key+        Right (write_eq "Key" (write_curly_brackets ("\\key" <-> written_key)))+    write_set_time :: String+    write_set_time = "\\time" <-> write_time time+    write_stave :: Stave note_type -> Either Error String+    write_stave (Stave {clef, tracks}) =+      do+        set_clef <- write_set_clef+        written_tracks <- write_tracks+        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", set_clef]) <->+                write_angular_brackets_2 written_tracks <->+                "\\bar \"|.\"")) where+      write_clef :: String+      write_clef =+        case clef of+          Percussion_clef -> "percussion"+          Pitched_clef {clef_name, transposition} -> write_clef_name clef_name <> write_transposition transposition+      write_set_clef :: Either Error String+      write_set_clef =+        do+          written_clef <- write_quotes write_clef+          Right ("\\clef" <-> written_clef)+      write_tracks :: Either Error String+      write_tracks =+        case tracks of+          One_track events -> write_track "\\oneVoice" (Pitched_note 4 C) events+          Two_tracks events_0 events_1 ->+            (+              (<->) <$>+              write_track "\\voiceOne" (Pitched_note 4 D) events_0 <*>+              write_track "\\voiceTwo" (Pitched_note 3 B) events_1)+    write_stave_group :: [Pitched_or_unpitched Instrument_and_staves] -> Either Error String+    write_stave_group stave_group =+      do+        instruments_and_staves <- traverse (pitched_and_unpitched write_instrument_and_staves) stave_group+        Right+          (case instruments_and_staves of+            [instrument_and_staves] -> instrument_and_staves+            _ -> "\\new StaffGroup" <-> write_angular_brackets_2 (intercalate " " instruments_and_staves))+    write_time_and_initial_position :: Either Error String+    write_time_and_initial_position =+      do+        set_initial_position <- write_set_initial_position+        Right (+          write_eq+            "Time_and_initial_position"+            (write_curly_brackets (intercalate " " (["\\numericTimeSignature", write_set_time] <> set_initial_position))))+    write_track :: String -> Note Pitched -> [Event note_type] -> Either Error String+    write_track voice unpitched_note events =+      do+        written_events <- write_events (convert_events_to_pitched events)+        Right ("\\new Voice" <-> write_curly_brackets (voice <-> write_curly_brackets written_events)) 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 events')+      convert_events_to_pitched :: [Event note_type] -> [Event Pitched]+      convert_events_to_pitched = (<$>) convert_event_to_pitched+      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 (mapMonotonic convert_note_to_pitched notes)+          Tie -> Tie+  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 {header, parts}) =+    do+      written_parts <- traverse write_part parts+      language <- write_language+      written_header <- write_header (Header_field' "tagline" "" : (convert_header_field <$> assocs header))+      Right+        (write_options <-> language <-> "\\layout {short-indent = 5\\mm}" <-> written_header <-> intercalate " " written_parts)+  write_time :: Time -> String+  write_time (Time num den) = write_time_numerator <> "/" <> write_basic_length den where+    write_time_numerator :: String+    write_time_numerator = show (time_numerator_to_int num)+  write_transposition :: Steps -> String+  write_transposition transposition =+    case compare transposition 0 of+      LT -> write_transposition' "_" (negate transposition)+      EQ -> ""+      GT -> write_transposition' "^" transposition+  write_transposition' :: String -> Steps -> String+  write_transposition' index transposition = index <> show transposition
Composition/MIDI.hs view
@@ -1,12 +1,12 @@ {-|-Description: Generating MIDI files.+Generates MIDI files. -} module Composition.MIDI (midi) where   import Composition.Errors+  import Composition.Notation   import Composition.Notes   import Composition.Score   import Composition.Theory-  import Composition.Time   import Control.Monad   import Control.Monad.Except   import Control.Monad.State.Strict@@ -45,7 +45,7 @@   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)+      0 -> throwError (Event_or_part_length_is_out_of_range_for_MIDI)       _ ->         do           let (i', last_byte) = divMod i 128@@ -73,7 +73,7 @@     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)+        check (MIDI_instrument_code_is_out_of_range_data (fromIntegral instrument)) (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) =@@ -90,7 +90,7 @@         case note of           Pitched_note _ _ ->             do-              check (Is_out_of_range_without_location Note_IOOR) (between min_note max_note note)+              check (Note_is_out_of_range_for_MIDI note) (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]@@ -138,8 +138,7 @@           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))+      quarter_note_length_in_microseconds = round ((60000000 * basic_length_denominator time_denominator) % (4 * tempo))     encode_pitched_track :: Track Pitched -> StateT [Word8] (Either Error) [Word8]     encode_pitched_track (Track {instrument, velocity, events}) =       do@@ -165,7 +164,7 @@   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)+    basic_length_denominator 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
+ Composition/Notation.hs view
@@ -0,0 +1,276 @@+module Composition.Notation (+  Event' (..),+  Event_fraction,+  Length_fraction,+  Position,+  Track (..),+  add_notational_information,+  events_length,+  initial_position_to_fraction,+  length_to_fraction,+  measure_length,+  remove_notational_information,+  scale_lengths,+  tracks_length,+  tracks_to_list) where+  import Composition.Errors+  import Composition.Notes+  import Composition.Score+  import Control.Lens.Combinators+  import Control.Monad+  import Control.Monad.Except+  import Data.Fixed+  import Data.Foldable+  import Data.Functor.Barbie+  import Data.Generics.Labels ()+  import Data.Maybe+  import Data.Ratio+  import Data.Set+  import GHC.Generics+  import GHC.Real+  import Parser.Utilities+  data Add_notational_information_notes note_type =+    Add_notational_information_rest |+    Add_notational_information_notes (Set (Note note_type)) |+    Add_notational_information_tie+  -- | 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 length as a fraction. Mostly for internal use.+  type Length_fraction = Ratio Int+  -- | The position of an event relative to the start of the measure. Mostly for internal use.+  type Position = Ratio 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]}+  deriving instance Generic (Event' notes)+  deriving instance Show (Add_notational_information_notes note_type)+  deriving instance (Show notes) => Show (Event' notes)+  deriving instance Show (Track note_type)+  -- | Add note length display information to events. Mostly for internal use.+  add_notational_information :: Time -> Initial_position -> [Event_fraction note_type] -> Either Error [Event note_type]+  add_notational_information time initial_position events =+    do+      traverse_ check_event_length events+      initial_position' <- initial_position_to_fraction time initial_position+      add_notational_information_events time initial_position' (transform_event <$> events)+  add_notational_information_basic_length :: Length_fraction -> Maybe Basic_length+  add_notational_information_basic_length (num :% den) =+    case num of+      1 -> denominator_to_basic_length den+      _ -> Nothing+  add_notational_information_events :: forall note_type .+    Time -> Position -> [Event' (Add_notational_information_notes note_type)] -> Either Error [Event note_type]+  add_notational_information_events time position events =+    join <$> traverse (add_notational_information_measure time) separate_measures where+    separate_measures :: [(Length_fraction, [Event' (Add_notational_information_notes note_type)])]+    separate_measures =+      case separate_measures' position events of+        [] -> []+        measure : measures' -> (position, measure) : ((,) 0 <$> measures')+    separate_measures' ::+      (+        Position ->+        [Event' (Add_notational_information_notes note_type)] ->+        [[Event' (Add_notational_information_notes note_type)]])+    separate_measures' 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' position'' events'' of+                  [] -> [[Event' notes len]]+                  measure : measures -> (Event' notes len : measure) : measures+              EQ -> [Event' notes len] : separate_measures' 0 events''+              GT ->+                let+                  len' = measure_length time - position' in+                  [Event' notes len'] : separate_measures' 0 (Event' (notes_to_tie notes) (len - len') : events'')+  add_notational_information_measure ::+    Time -> (Position, [Event' (Add_notational_information_notes note_type)]) -> Either Error [Event note_type]+  add_notational_information_measure time (position, events) =+    do+      binary_blocks <- separate_binary_blocks position events+      join <$> traverse add_notational_information_binary_block binary_blocks where+    add_notational_information_binary_block ::+      (Position, [Event' (Add_notational_information_notes note_type)]) -> Either Error [Event note_type]+    add_notational_information_binary_block (position', events') =+      (case events' of+        [event@(Event' notes len)] ->+          case add_notational_information_simple_length len of+            Nothing -> divide_time position' [event]+            Just len' -> Right [Event (add_notational_information_notes notes) len']+        _ ->+          let+            num :% den = events_length events' in+            (+              (\ events'' -> [Triplet events'']) <$>+              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' -> add_notational_information_measure (Time [Three] den') (0, scale_lengths (3 % 2) events')+                _ -> divide_time position' events'))+    divide_time :: Position -> [Event' (Add_notational_information_notes note_type)] -> Either Error [Event note_type]+    divide_time position' events' =+      do+        time' <- time_subdivision time+        add_notational_information_events time' (mod' position' (measure_length time')) events'+    separate_binary_blocks ::+      (+        Position ->+        [Event' (Add_notational_information_notes note_type)] ->+        Either Error [(Position, [Event' (Add_notational_information_notes note_type)])])+    separate_binary_blocks position' events' =+      case events' of+        [] -> Right []+        event@(Event' _ len) : events'' ->+          do+            let position'' = position' + len+            binary_blocks <- separate_binary_blocks position'' events''+            case is_smooth [2] position'' of+              False ->+                case binary_blocks of+                  [] -> Left Track_ends_with_an_incomplete_triplet+                  (_, events''') : binary_blocks' -> Right ((position', event : events''') : binary_blocks')+              True -> Right ((position', [event]) : binary_blocks)+  add_notational_information_notes :: Add_notational_information_notes note_type -> Notes note_type+  add_notational_information_notes notes =+    case notes of+      Add_notational_information_rest -> Notes empty+      Add_notational_information_notes notes' -> Notes notes'+      Add_notational_information_tie -> Tie+  add_notational_information_simple_length :: Length_fraction -> Maybe Length+  add_notational_information_simple_length (num :% den) =+    do+      dots <- (\ i -> i - 1) <$> lg (1 + num)+      len <- add_notational_information_basic_length (2 ^ dots % den)+      Just (Length len (replicate dots Dot))+  check_event_length :: Event' notes -> Either Error ()+  check_event_length (Event' _ len) =+    do+      check Non_positive_note_length (0 < len)+      check Note_length_denominator_contains_factors_other_than_2_and_3 (is_smooth [2, 3] len)+      return ()+  -- | Events length.+  events_length :: [Event' notes] -> Length_fraction+  events_length events = sum (view #event_length <$> events)+  -- | Convert initial position to fraction. Mostly for internal use.+  initial_position_to_fraction :: (MonadError Error f) => Time -> Initial_position -> f Position+  initial_position_to_fraction time (Initial_position num den) =+    do+      let initial_position' = num % basic_length_denominator den+      check+        Initial_position_is_out_of_range+        (between 0 (measure_length time - length_to_fraction (Length maxBound [])) initial_position')+      return initial_position'+  is_smooth :: [Int] -> Ratio Int -> Bool+  is_smooth factors x = is_smooth' factors (denominator x)+  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 (elems notes_0, elems notes_1) of+          ([], []) -> Nothing+          _ -> Just notes_1+      Tie -> Nothing+  -- | Convert length to fraction. Mostly for internal use.+  length_to_fraction :: Length -> Length_fraction+  length_to_fraction (Length len dots) = (1 % basic_length_denominator len) * (2 - 1 % 2 ^ length dots)+  -- | The length of one measure. Mostly for internal use.+  measure_length :: Time -> Length_fraction+  measure_length (Time num den) = time_numerator_to_int num % basic_length_denominator den+  notes_to_set :: Notes note_type -> Set (Note note_type)+  notes_to_set maybe_notes =+    case maybe_notes of+      Notes notes -> notes+      Tie -> empty+  notes_to_tie :: Add_notational_information_notes note_type -> Add_notational_information_notes note_type+  notes_to_tie notes =+    case notes of+      Add_notational_information_rest -> Add_notational_information_rest+      Add_notational_information_notes _ -> Add_notational_information_tie+      Add_notational_information_tie -> Add_notational_information_tie+  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_and_staves]] -> [Pitched_or_unpitched Track]+  remove_notational_information stave_groups =+    stave_groups >>= flip (>>=) (btraverse remove_notational_information_instrument_and_staves)+  remove_notational_information_stave :: Stave note_type -> [[Event_fraction note_type]]+  remove_notational_information_stave (Stave {tracks}) = remove_notational_information_events <$> tracks_to_list tracks+  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_and_staves :: Instrument_and_staves note_type -> [Track note_type]+  remove_notational_information_instrument_and_staves (Instrument_and_staves {midi_instrument, velocity, staves}) =+    do+      events <- staves >>= remove_notational_information_stave+      [Track {instrument = midi_instrument, velocity, events}]+  -- | 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+  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)+  track_length :: Track note_type -> Ratio Int+  track_length (Track {events}) = events_length events+  -- | Calculate part length. Mostly for internal use.+  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)+  -- | Convert the stave to a list of voices. Mostly for internal use.+  tracks_to_list :: Tracks note_type -> [[Event note_type]]+  tracks_to_list stave =+    case stave of+      One_track events -> [events]+      Two_tracks events_0 events_1 -> [events_0, events_1]+  transform_event :: Event_fraction note_type -> Event' (Add_notational_information_notes note_type)+  transform_event (Event' notes len) = Event' (transform_notes notes) len+  transform_notes :: Set (Note note_type) -> Add_notational_information_notes note_type+  transform_notes notes =+    case elems notes of+      [] -> Add_notational_information_rest+      _ -> Add_notational_information_notes notes
Composition/Notes.hs view
@@ -8,10 +8,7 @@   Branch (..),   Dot (..),   Event (..),-  Event' (..),-  Event_fraction,   Length (..),-  Length_fraction,   Natural_note_name (..),   Note (..),   Note_name (..),@@ -20,127 +17,93 @@   Pitched_or_unpitched (..),   accidentals,   basic_length_denominator,-  basic_length_to_fraction,   construct_note_name,   deconstruct_note_name,   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 ---+  pitched_or_unpitched) where+  import Data.Functor.Barbie+  import Data.Maybe   import Data.Set-  import Data.Tuple ----  import GHC.Generics ----  import Parser.Utilities ---+  import Data.Tuple+  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 ---+  -- | Accidentals.+  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+  -- | 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]+  -- | Note lengths.+  data Length = Length Basic_length [Dot]+  -- | Natural note names.+  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 Bounded Basic_length+  deriving instance Bounded Note_name   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 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 Eq Accidental+  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 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) ---+  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 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)] ---+  -- | Convert basic length to fraction denominator. Mostly for internal use.+  basic_length_denominator :: Basic_length -> Int+  basic_length_denominator len = 2 ^ fromEnum len   -- | 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 =@@ -152,44 +115,38 @@       "" -> 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 ---+  -- | 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+  -- | 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 =+    do+      check () (between minBound maxBound i)+      Just (toEnum i)+  -- | Divide basic note length by two.+  next_basic_length :: Basic_length -> Maybe Basic_length+  next_basic_length len = lg_denominator_to_basic_length (1 + fromEnum 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       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 ---
Composition/Parser.hs view
@@ -1,11 +1,10 @@ {-|-Parse the custom file format for scores.+Parse the custom score file format. -} 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@@ -33,17 +32,15 @@     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_and_staves_token |     Instrument_name_token |     Key_token |     Left_curly_bracket_token |@@ -60,15 +57,18 @@     Right_curly_bracket_token |     Right_square_bracket_token |     Score_token |+    Short_instrument_name_token |     Slash_token |-    Stave_groups_token |     Stave_token |+    Stave_groups_token |+    Staves_token |     Tempo_token |     Text_token String |     Tie_token |     Time_name_token |     Time_value_token |     Title_token |+    Tracks_token |     Transposition_token |     Unpitched_note_token |     Unpitched_token |@@ -91,22 +91,16 @@       '-' -> 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+      Nothing -> Left (Duplicate_header_fields <$> Just)       Just header' -> Right header'   construct_key :: [Note_name] -> Either (Location -> Error) (Set Note_name)   construct_key key =@@ -121,7 +115,7 @@   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)+      Nothing -> Left (Invalid_word word <$> Just)       Just token -> Right token) where     construct_clef_name :: Maybe Token     construct_clef_name = Clef_name_value_token <$> readMaybe word@@ -130,29 +124,30 @@     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+        "Instrument_and_staves" -> Just Instrument_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+        "Stave" -> Just Stave_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+        "short_instrument_name" -> Just Short_instrument_name_token         "stave_groups" -> Just Stave_groups_token+        "staves" -> Just Staves_token         "tempo" -> Just Tempo_token         "time" -> Just Time_name_token         "title" -> Just Title_token+        "tracks" -> Just Tracks_token         "transposition" -> Just Transposition_token         "velocity" -> Just Velocity_token         "x" -> Just Unpitched_note_token@@ -166,15 +161,20 @@             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_basic_length :: Int -> Either (Location -> Error) Basic_length+  convert_basic_length len =+    case denominator_to_basic_length len of+      Nothing -> Left (Invalid_note_length len)+      Just len' -> Right len'   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)+      check (MIDI_instrument_code_is_out_of_range_score midi_instrument) (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+      Nothing -> Left (Invalid_time_numerator_factor time_numerator_factor)       Just time_numerator_factor' -> Right time_numerator_factor'   convert_velocity :: Int -> Either (Location -> Error) Velocity   convert_velocity velocity =@@ -233,13 +233,14 @@     case token of       Note_name_token note_name -> Just note_name       _ -> Nothing+  -- | Read the score from a .aoi file.   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))+      except (fromJust (parse' classify_char next_location tokenise parse_score Parse_error_score score))   parse_basic_length :: Parser Basic_length-  parse_basic_length = fmap_filter_parser construct_basic_length parse_positive_int+  parse_basic_length = fmap_filter_parser convert_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@@ -270,36 +271,27 @@   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 =+  parse_instrument_and_staves :: Parser (Pitched_or_unpitched Instrument_and_staves)+  parse_instrument_and_staves =+    Pitched <$> parse_instrument_and_staves' Branch_pitched <+> Unpitched <$> parse_instrument_and_staves' Branch_unpitched+  parse_instrument_and_staves' :: forall note_type . Branch note_type -> Parser (Instrument_and_staves note_type)+  parse_instrument_and_staves' branch =     do       parse_pitched_or_unpitched       parse_struct-        Instrument_clefs_and_staves_token+        Instrument_and_staves_token         (do           instrument_name <- parse_field Instrument_name_token parse_text+          short_instrument_name <- parse_field Short_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+          staves <- parse_field Staves_token (parse_list' parse_stave)+          return (Instrument_and_staves {instrument_name, short_instrument_name, midi_instrument, velocity, 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)@@ -315,15 +307,23 @@     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 :: Parser (Tracks 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_stave =+      parse_struct+        Stave_token+        (do+          clef <- parse_field Clef_token parse_clef+          tracks <- parse_field Tracks_token parse_tracks+          return (Stave {clef, tracks}))+    parse_tracks :: Parser (Tracks note_type)+    parse_tracks = 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 :: Parser (Tracks note_type)     parse_two_tracks = Two_tracks <$> parse_events <*> parse_events     pitched_or_unpitched_token :: Token -> Maybe ()     pitched_or_unpitched_token token =@@ -364,7 +364,7 @@         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))+        stave_groups <- parse_field Stave_groups_token (parse_list' (parse_list' parse_instrument_and_staves))         return (Part {title, key, time, initial_position, tempo, stave_groups}))   parse_pitched_clef :: Parser (Clef Pitched)   parse_pitched_clef =@@ -383,10 +383,9 @@     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}))+        return (Score {header, parts}))   parse_slash :: Parser ()   parse_slash = parse_token Slash_token   parse_square_brackets :: Parser t -> Parser t
Composition/Score.hs view
@@ -1,301 +1,117 @@-{-| ----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 ---+{-|+Scores that can be converted to Lilypond and MIDI files.+-}+module Composition.Score (+  Clef (..),+  Clef_name (..),+  Header_field (..),+  Initial_position (..),+  Instrument_and_staves (..),+  Key,+  MIDI_instrument,+  Part (..),+  Score (..),+  Stave (..),+  Tempo,+  Time (..),+  Time_numerator_factor (..),+  Tracks (..),+  Velocity,+  int_to_time_numerator_factor,+  max_velocity,+  time_numerator_factor_to_int,+  time_numerator_to_int) where+  import Composition.Notes+  import Composition.Theory+  import Data.List as List+  import Data.Map.Strict+  import Data.Maybe+  import Data.Set as Set+  import Data.Tuple+  import Data.Word+  -- | 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+  -- | Score header fields.+  data Header_field = Left_text | Overtitle | Right_text | Subtitle | Subsubtitle | Title+  -- | 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 partial 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+  -- | Group of staves for one instrument.+  data Instrument_and_staves note_type =+    Instrument_and_staves {+      instrument_name :: String,+      short_instrument_name :: String,+      midi_instrument :: MIDI_instrument,+      velocity :: Velocity,+      staves :: [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]@. Conflicting accidentals will result in an error.+  type Key = Set Note_name+  -- | MIDI instrument code.+  type MIDI_instrument = Word8+  -- | 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_and_staves]]}+  -- | Score that can be converted to Lilypond and MIDI files.+  data Score = Score {header :: Map Header_field String, parts :: [Part]}+  -- | Stave with clef.+  data Stave note_type = Stave {clef :: Clef note_type, tracks :: Tracks note_type}+  -- | Tempo in beats per minute.+  type Tempo = 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+  -- | One stave can fit up to two rhythmically independent tracks.+  data Tracks note_type = One_track [Event note_type] | Two_tracks [Event note_type] [Event note_type]+  -- | MIDI volume ranges from 0 to 127.+  type Velocity = Word8+  deriving instance Eq Clef_name+  deriving instance Eq Header_field+  deriving instance Eq Time_numerator_factor+  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 ---+  deriving instance Show (Clef note_type)+  deriving instance Show Clef_name+  deriving instance Show Header_field+  deriving instance Show Initial_position+  deriving instance Show (Instrument_and_staves note_type)+  deriving instance Show Part+  deriving instance Show Score+  deriving instance Show (Stave note_type)+  deriving instance Show Time+  deriving instance Show Time_numerator_factor+  deriving instance Show (Tracks note_type)+  -- | 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 = List.lookup time_numerator_factor (swap <$> time_numerator_factors)+  -- | Maximum MIDI volume.+  max_velocity :: Velocity+  max_velocity = 127+  -- | 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 (List.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)
Composition/Theory.hs view
@@ -1,5 +1,5 @@ {-|-Basic music theory.+Basic music theory: intervals. -} module Composition.Theory (   Interval (..),
− Composition/Time.hs
@@ -1,70 +0,0 @@-{-| ----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)
Composition/Write.hs view
@@ -1,137 +1,123 @@-{-| ----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) ---+{-|+Write the custom file format.+-}+module Composition.Write (write, write_accidental, write_natural_note_name, write_note) where+  import Composition.Errors+  import Composition.Notation+  import Composition.Notes+  import Composition.Score+  import Control.Monad.Except+  import Data.List as List+  import Data.Map.Strict+  import Data.Maybe+  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 an accidental. Mostly for internal use.+  write_accidental :: Accidental -> String+  write_accidental accidental = fromJust (List.lookup accidental accidentals)+  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_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_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_and_staves :: Instrument_and_staves note_type -> String+  write_instrument_and_staves+    (Instrument_and_staves {instrument_name, short_instrument_name, midi_instrument, velocity, staves}) =+      write_struct+        "Instrument_and_staves"+        (+          write_eq "instrument_name" (write_quotes instrument_name) <->+          write_eq "short_instrument_name" (write_quotes short_instrument_name) <->+          write_eq "midi_instrument" (show midi_instrument) <->+          write_eq "velocity" (show velocity) <->+          write_eq "staves" (write_list write_stave staves))+  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 a natural note name. Mostly for internal use.+  write_natural_note_name :: Natural_note_name -> String+  write_natural_note_name natural_note_name = [head (show natural_note_name)]+  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 a note name. Mostly for internal use.+  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 <->+        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_and_staves)) stave_groups)) where+    write_key :: String+    write_key = write_list write_note_name (Set.elems key)+  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 {header, parts}) =+    write_struct "Score" (write_eq "header" write_header <-> write_eq "parts" (write_list write_part parts)) where+    write_header :: String+    write_header = write_list write_header_field (assocs header)+  write_square_brackets :: String -> String+  write_square_brackets = write_brackets "[" "]"+  write_stave :: Stave note_type -> String+  write_stave (Stave {clef, tracks}) =+    write_struct "Stave" (write_eq "clef" write_clef <-> write_eq "tracks" write_tracks) where+    write_clef :: String+    write_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_tracks :: String+    write_tracks = write_list write_events (tracks_to_list tracks)+  write_struct :: String -> String -> String+  write_struct name fields = name <-> write_curly_brackets 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)
+ Keyboard.key view
@@ -0,0 +1,52 @@+[] [0 1 2 3 4 5 6 7 8 9 10 11]+[2] [0 1 2 3 4 5 6 7 8 9 10 11]+[2 3] [0 1 2 3 4 5 6 7 8 9 10 11]+[2 4] [0 1 2 3 4 5 6 7 8 9 10 11]+[2 6] [0 1 2 3 4 5 6 7 8 9 10 11]+[2 7] [0 1 2 3 4 5 6 7 8 9 10 11]+[2 10] [0 1 2 3 4 5 6 7 8 9 10 11]+[3] [0 1 2 3 4 5 6 7 8 9 10 11]+[3 2] [0 1 2 3 4 5 6 7 8 9 10 11]+[3 3] [0 1 2 3 4 5 6 7 8 9 10 11]+[3 4] [0 1 2 3 4 5 6 7 8 9 10 11]+[3 5] [0 1 2 3 4 5 6 7 8 9 10 11]+[3 6] [0 1 2 3 4 5 6 7 8 9 10 11]+[3 7] [0 1 2 3 4 5 6 7 8 9 10 11]+[3 9] [0 1 2 3 4 5 6 7 8 9 10 11]+[4] [0 1 2 3 4 5 6 7 8 9 10 11]+[4 2] [0 1 2 3 4 5 6 7 8 9 10 11]+[4 3] [0 1 2 3 4 5 6 7 8 9 10 11]+[4 5] [0 1 2 3 4 5 6 7 8 9 10 11]+[4 6] [0 1 2 3 4 5 6 7 8 9 10 11]+[4 8] [0 1 2 3 4 5 6 7 8 9 10 11]+[4 10] [0 1 2 3 5 6 7 8 9 10]+[5] [0 1 2 3 4 5 6 7 8 9 10 11]+[5 3] [0 1 2 3 4 5 6 7 8 9 10 11]+[5 4] [0 1 2 3 4 5 6 7 8 9 10 11]+[5 7] [0 1 2 3 4 5 6 7 8 9 10 11]+[5 9] [0 1 2 3 5 6 7 8 9 10]+[5 10] [3 10]+[6] [0 1 2 3 4 5 6 7 8 9 10 11]+[6 2] [0 1 2 3 4 5 6 7 8 9 10 11]+[6 3] [0 1 2 3 4 5 6 7 8 9 10 11]+[6 4] [0 1 2 3 4 5 6 7 8 9 10 11]+[6 8] [0 1 2 3 5 6 7 8 9 10]+[6 9] [3 10]+[7] [0 1 2 3 4 5 6 7 8 9 10 11]+[7 2] [0 1 2 3 4 5 6 7 8 9 10 11]+[7 3] [0 1 2 3 4 5 6 7 8 9 10 11]+[7 5] [0 1 2 3 4 5 6 7 8 9 10 11]+[7 8] [3 10]+[8] [0 1 2 3 4 5 6 7 8 9 10 11]+[8 4] [0 1 2 3 4 5 6 7 8 9 10 11]+[8 6] [0 1 2 3 5 6 7 8 9 10]+[8 7] [3 10]+[9] [0 1 2 3 4 5 6 7 8 9 10 11]+[9 3] [0 1 2 3 4 5 6 7 8 9 10 11]+[9 5] [0 1 2 3 5 6 7 8 9 10]+[9 6] [3 10]+[10] [0 1 2 3 4 5 6 7 8 9 10 11]+[10 4] [0 1 2 3 5 6 7 8 9 10]+[12] [0 1 2 3 4 5 6 7 8 9 10 11]+[14] [0 1 2 3 5 6 7 8 9 10]+[15] [3 10]
Main.hs view
@@ -1,267 +1,367 @@-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 --+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.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.Tuple+  import Parser.Files+  import Parser.Locations+  import Parser.Parser+  import Parser.Utilities+  import System.Environment+  import Text.Read+  data Char_class =+    Delimiter_char Token |+    File_path_char Char |+    Invalid_char |+    Nonzero_nat_char Char |+    Quote_char |+    Text_char Char |+    Whitespace_char |+    Word_char Char |+    Zero_char+  data Command =+    Keyboard_command {+      playables :: File_path,+      source :: File_path,+      header :: Map Header_field String,+      midi_instrument :: MIDI_instrument,+      destination :: File_path} |+    Lilypond_command {source :: File_path, destination :: File_path} |+    MIDI_command {source :: File_path, destination :: File_path}+  type Parser = Parser' Token Error+  data Token =+    Destination_token |+    Eq_token |+    File_path_token String |+    Header_field_token Header_field |+    Header_token |+    Keyboard_token |+    Left_curly_bracket_token |+    Left_square_bracket_token |+    Lilypond_token |+    MIDI_instrument_token |+    MIDI_token |+    Nat_token Int |+    Playables_token |+    Right_curly_bracket_token |+    Right_square_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 -> Word_char c+      _ | elem c "./" -> File_path_char c+      '0' -> Zero_char+      _ | isDigit c && c /= '0' -> Nonzero_nat_char c+      _ | elem c (fst <$> delimiters) -> Delimiter_char (fromJust (List.lookup c delimiters))+      _ -> Invalid_char+  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 Nothing)+      Just header' -> Right header'+  construct_word :: String -> Either (Location -> Error) Token+  construct_word word =+    (case construct_header_field <|> construct_keyword of+      Nothing -> Left (\ _ -> Invalid_word word Nothing)+      Just token -> Right token) where+    construct_header_field :: Maybe Token+    construct_header_field = Header_field_token <$> readMaybe word+    construct_keyword :: Maybe Token+    construct_keyword =+      case word of+        "Keyboard" -> Just Keyboard_token+        "Lilypond" -> Just Lilypond_token+        "MIDI" -> Just MIDI_token+        "destination" -> Just Destination_token+        "header" -> Just Header_token+        "midi_instrument" -> Just MIDI_instrument_token+        "playables" -> Just Playables_token+        "source" -> Just Source_token+        _ -> Nothing+  convert_midi_instrument :: Int -> Either (Location -> Error) MIDI_instrument+  convert_midi_instrument midi_instrument =+    do+      check (\ _ -> MIDI_instrument_code_is_out_of_range_command midi_instrument) (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+  delimiters :: [(Char, Token)]+  delimiters =+    [+      ('=', Eq_token),+      ('[', Left_square_bracket_token),+      (']', Right_square_bracket_token),+      ('{', Left_curly_bracket_token),+      ('}', Right_curly_bracket_token)]+  file_path_char :: Char_class -> Maybe Char+  file_path_char char_class =+    case char_class of+      File_path_char c -> Just c+      Nonzero_nat_char c -> Just c+      Word_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+  header_field_token :: Token -> Maybe Header_field+  header_field_token token =+    case token of+      Header_field_token header_field -> Just header_field+      _ -> Nothing+  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' command =+    (do+      command' <- except parse_command+      case command' of+        Keyboard_command {playables = playables_file_path, source, header, midi_instrument, destination} ->+          do+            playables <- parse_playables playables_file_path+            original_score <- parse source+            keyboard_score <- except (reduction playables header midi_instrument original_score)+            write destination keyboard_score+        Lilypond_command {source, destination} ->+          do+            score <- parse source+            lilypond score destination+        MIDI_command {source, destination} ->+          do+            score <- parse source+            midi score destination) where+    parse_command :: Either Error Command+    parse_command =+      fromJust (parse' classify_char (return next_char) tokenise parse_command' (\ _ -> Parse_error_command) command)+  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+  nat_token :: Token -> Maybe Int+  nat_token token =+    case token of+      Nat_token i -> Just i+      _ -> Nothing+  nonzero_nat_char :: Char_class -> Maybe Char+  nonzero_nat_char char_class =+    case char_class of+      Nonzero_nat_char c -> Just c+      _ -> Nothing+  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_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_keyboard :: Parser Command+  parse_keyboard =+    parse_struct+      Keyboard_token+      (do+        playables <- parse_field Playables_token (parse_file_path "key")+        source <- parse_field Source_token (parse_file_path "aoi")+        header <- parse_field Header_token parse_header+        midi_instrument <- parse_field MIDI_instrument_token parse_midi_instrument+        destination <- parse_field Destination_token (parse_file_path "aoi")+        return (Keyboard_command {playables, source, header, 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_command {source, destination}))+  parse_list' :: Parser t -> Parser [t]+  parse_list' parse_t = parse_square_brackets (parse_many parse_t)+  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_command {source, destination}))+  parse_midi_instrument :: Parser MIDI_instrument+  parse_midi_instrument = fmap_filter_parser convert_midi_instrument parse_nat+  parse_nat :: Parser Int+  parse_nat = parse_token' nat_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 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 -> List.lookup token (swap <$> delimiters)+      File_path_char c -> Just c+      Invalid_char -> Nothing+      Nonzero_nat_char c -> Just c+      Quote_char -> Nothing+      Text_char c -> Just c+      Whitespace_char -> Just ' '+      Word_char c -> Just c+      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_word) <+ 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_nat :: Tokeniser ()+  tokenise_nat = add_token (Nat_token <$> (tokenise_zero <+> tokenise_positive_int))+  tokenise_positive_int :: Tokeniser Int+  tokenise_positive_int = read <$> ((:) <$> parse_token' nonzero_nat_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_word :: Tokeniser ()+  tokenise_word = add_token (fmap_filter_parser construct_word (parse_some (parse_token' word_char)))+  tokenise_zero :: Tokeniser Int+  tokenise_zero =+    do+      parse_token Zero_char+      return 0+  word_char :: Char_class -> Maybe Char+  word_char char_class =+    case char_class of+      Word_char c -> Just c+      _ -> Nothing+  -- | 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_accidentals_in_key_signature -> "Conflicting accidentals in key signature."+      Duplicate_header_fields location -> "Duplicate header fields" <-> write_maybe_location location <> "."+      Duplicate_keys location -> "Duplicate keys at" <-> write_location location <> "."+      Duplicate_note_names_in_key_signature location ->+        "Duplicate note name in key signature at" <-> write_location location <> "."+      Duplicate_notes location -> "Duplicate notes at" <-> write_location location <> "."+      Empty_triplet -> "Empty triplet."+      Event_or_part_length_is_out_of_range_for_MIDI -> "Event or part length is out of range for MIDI."+      File_error err' ->+        case err' of+          Failed_to_find_file file_path -> "Failed to find file" <-> file_path <> "."+          Invalid_file_path -> "Invalid file path."+          Unexpected_extension ext -> "Unexpected extension ." <> ext <> "."+      Initial_position_is_out_of_range -> "Initial position is out of range."+      Key_is_out_of_range key location -> "Key" <-> show key <-> "at" <-> write_location location <-> "is out of range."+      Invalid_character_in_text -> "Invalid character in text."+      Invalid_note_length len location -> "Invalid note length" <-> show len <-> "at" <-> write_location location <> "."+      Invalid_note_name natural_note_name accidental location ->+        (+          "Invalid note name" <->+          write_natural_note_name natural_note_name <>+          write_accidental accidental <->+          "at" <->+          write_location location <>+          ".")+      Invalid_time_numerator_factor time_numerator_factor location ->+        "Invalid time numerator factor" <-> show time_numerator_factor <-> "at" <-> write_location location <> "."+      Invalid_word word location -> "Invalid word" <-> word <-> write_maybe_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."+      MIDI_instrument_code_is_out_of_range_command instrument ->+        "MIDI instrument code" <-> show instrument <-> "in the command is out of range."+      MIDI_instrument_code_is_out_of_range_data instrument ->+        "MIDI instrument code" <-> show instrument <-> "is out of range."+      MIDI_instrument_code_is_out_of_range_score instrument location ->+        "MIDI instrument code" <-> show instrument <-> "in the score at" <-> write_location location <-> "is out of range."+      Non_positive_note_length -> "Non-positive note length."+      Note_is_out_of_range_for_MIDI note -> "Note" <-> write_note note <-> "is out of range for MIDI."+      Note_length_denominator_contains_factors_other_than_2_and_3 ->+        "Note length denominator contains factors other than 2 and 3."+      Parse_error_command -> "Parse error in the command."+      Parse_error_playables location -> "Parse error in the playables file at" <-> write_location location <> "."+      Parse_error_score location -> "Parse error in the score 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+      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"+  write_maybe_location :: Maybe Location -> String+  write_maybe_location maybe_location =+    case maybe_location of+      Nothing -> "in the command"+      Just location -> "at" <-> write_location location
+ README.md view
@@ -0,0 +1,272 @@+# Aoide++## Score file++Score files have to have a `.aoi` extension.++The best way to learn the syntax is to have a look at the example scores in `Scores` directory.++Score files aren't indentation-sensitive. Structure field order is fixed - for example, when you create a score the header comes+first and the parts come second.++### `SCORE`++~~~+Score {+  header = (HEADER)+  parts = [+    (PART)+    (PART)+    (PART)+    ...]}+~~~++### `HEADER`++~~~+[(HEADER_FIELD) (HEADER_FIELD) (HEADER_FIELD) ...]+~~~++You do not need to use all the possible header fields - use only the ones that you need. Header field order is flexible.++### `HEADER_FIELD`++~~~+(HEADER_FIELD_NAME) = ""+~~~++### `HEADER_FIELD_NAME`++* `Left_text`+* `Overtitle`+* `Right_text`+* `Subtitle`+* `Subsubtitle`+* `Title`++### `PART`++~~~+Part {+  title = ""+  key = (KEY)+  time = (TIME)+  initial_position = (INITIAL_POSITION)+  tempo = 100+  stave_groups = [+    (STAVE_GROUP)+    (STAVE_GROUP)+    (STAVE_GROUP)+    ...]}+~~~++### `KEY`++~~~+[(NOTE_NAME) (NOTE_NAME) (NOTE_NAME) ...]+~~~++You only need to specify the note names that have accidentals.++### `NOTE_NAME`++* `C`+* `C#`+* `Db`+* ...++### `TIME`++* `Time [2] 2` - 2/2+* `Time [2 2] 4` - 4/4+* `Time [2 2 2] 8` - 8/8+* `Time [2 2 3] 16` - 12/16+* `Time [2 3] 8` - 6/8+* `Time [3] 4` - 3/4+* `Time [3 3] 16` - 9/16+* ...++### `INITIAL_POSITION`++The timing of the first event relative to the start of the measure. 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 pickup bar, the initial position can be written as 1/2 or 2/4.++### `STAVE_GROUP`++~~~+[+  (INSTRUMENT_AND_STAVES)+  (INSTRUMENT_AND_STAVES)+  (INSTRUMENT_AND_STAVES)+  ...]+~~~++### `INSTRUMENT_AND_STAVES`++~~~+(PITCHED_OR_UNPITCHED) Instrument_and_staves {+  instrument_name = ""+  short_instrument_name = ""+  midi_instrument = 0+  velocity = 127+  staves = [+    (STAVE)+    (STAVE)+    (STAVE)+    ...]}+~~~++### `PITCHED_OR_UNPITCHED`++* `Pitched`+* `Unpitched`++### `STAVE`++~~~+Stave {+  clef = (CLEF)+  tracks = (TRACKS)}+~~~++### `CLEF`++~~~+Percussion_clef+~~~++~~~+Pitched_clef {clef_name = (CLEF_NAME) transposition = 0}+~~~++### `CLEF_NAME`++* `Subbass`+* `Bass`+* `Baritone_F`+* `Baritone_C`+* `Tenor`+* `Alto`+* `Mezzosoprano`+* `Soprano`+* `Treble`+* `French`++### `TRACKS`++~~~+[+  EVENTS]+~~~++~~~+[+  EVENTS+  EVENTS]+~~~++One stave can contain one or two tracks.++### `EVENTS`++~~~+[(EVENT) (EVENT) (EVENT) ...]+~~~++### `EVENT`++~~~+(NOTES)(LENGTH)+~~~++~~~+(EVENTS)+~~~++An event can be notes with a length or a triplet.++### `NOTES`++~~~+[(NOTE) (NOTE) (NOTE) ...]+~~~++~~~+++~~~++Notes can be either a set of new notes or a tie.++### `NOTE`++~~~+(NOTE_NAME)(OCTAVE)+~~~++### `LENGTH`++~~~+(BASIC_LENGTH)(DOTS)+~~~++### `BASIC_LENGTH`++* `1` - Whole+* `2` - Half+* `4` - Quarter+* ...++### `DOTS`++*+* `.`+* `..`+* ...++## Playables file++Playables file is needed if you want to generate keyboard reductions. A default playables file `Keyboard.key` is provided and+you can modify it according to your own preferred style of music (you might need to account for a greater variety of dissonant+chords) and your own hand hand span.++### `PLAYABLES`++~~~+(PLAYABLE)+(PLAYABLE)+(PLAYABLE)+...+~~~++### `PLAYABLE`++~~~+[(STEPS) (STEPS) (STEPS) ...] [(KEY) (KEY) (KEY) ...]+~~~++Each++## Commands++The whole command should be wrapped in quotation marks and given as one string argument to the program. Command argument order+is fixed.++File paths should be given with no quotation marks.++### Lilypond++~~~+Lilypond {source = (FILE_PATH) destination = (FILE_PATH)}+~~~++### MIDI++~~~+MIDI {source = (FILE_PATH) destination = (FILE_PATH)}+~~~++### Keyboard reduction++~~~+Keyboard {playables = (FILE_PATH) source = (FILE_PATH) header = (HEADER) midi_instrument = 0 destination = (FILE_PATH)}+~~~
+ Scores/Sileant_zephyri/Sileant_zephyri.aoi view
@@ -0,0 +1,438 @@+Score {+  header = [Right_text = "Antonio Vivaldi" Subtitle = "Filiae maestae Jerusalem" Subsubtitle = "RV 638" Title = "Sileant zephyri"]+  parts = [+    Part {+      title = ""+      key = [Db Eb Ab Bb]+      time = Time [3] 4+      initial_position = 0+      tempo = 90+      stave_groups = [+        [+          Pitched Instrument_and_staves {+            instrument_name = "Violin I"+            short_instrument_name = "vn I"+            midi_instrument = 40+            velocity = 127+            staves = [+              Stave {+                clef = Pitched_clef {clef_name = Treble transposition = 0}+                tracks = [+                  [+                    [Ab5]8 [Ab5]8 [Ab4]8 [Ab4]8 [Ab5]8 [Ab5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [Bb5]8 [Bb5]8 [G4]8 [G4]8 [Bb5]8 [Bb5]8+                    [Ab5]8 [Ab5]8 [Ab4]8 [Ab4]8 [Ab5]8 [Ab5]8+                    [Ab5]8 [Ab5]8 [D5]8 [D5]8 [Ab5]8 [Ab5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [C5]8 [C5]8 [G5]8 [G5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [Ab5]8 [Ab5]8 [Ab4]8 [Ab4]8 [Ab5]8 [Ab5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [Ab5]4 []2+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    [Eb5]8 [Eb5]8 [Eb4]8 [Eb4]8 [Eb5]8 [Eb5]8+                    [D5]8 [D5]8 [D4]8 [D4]8 [D5]8 [D5]8+                    [F5]8 [F5]8 [B4]8 [B4]8 [F5]8 [F5]8+                    [Eb5]4 []2+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [F5]8 [F5]8 [C5]8 [C5]8 [F5]8 [F5]8+                    [F5]8 [F5]8 [C5]8 [C5]8 [F5]8 [F5]8+                    [F5]8 [F5]8 [Db4]8 [Db4]8 [F5]8 [F5]8+                    [G5]8 [G5]8 [Db5]8 [Db5]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [Db5]8 [Db5]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [E4]8 [E4]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [E4]8 [E4]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [E4]8 [E4]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [E4]8 [E4]8 [G5]8 [G5]8+                    [F5]4 []2+                    []2.+                    [Ab5]8 [Ab5]8 [Ab4]8 [Ab4]8 [Ab5]8 [Ab5]8+                    [G5]8 [G5]8 [E4]8 [E4]8 [G5]8 [G5]8+                    [F5]4 []2+                    []2.+                    [Ab5]8 [Ab5]8 [Ab4]8 [Ab4]8 [Ab5]8 [Ab5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [Bb5]8 [Bb5]8 [G4]8 [G4]8 [Bb5]8 [Bb5]8+                    [Ab5]8 [Ab5]8 [Ab4]8 [Ab4]8 [Ab5]8 [Ab5]8+                    [Ab5]8 [Ab5]8 [D5]8 [D5]8 [Ab5]8 [Ab5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [C5]8 [C5]8 [G5]8 [G5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [Ab5]8 [Ab5]8 [Ab4]8 [Ab4]8 [Ab5]8 [Ab5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [Ab5]4 []2+                    []2.+                    []2.+                    [Ab5]8 [Ab5]8 [Ab4]8 [Ab4]8 [Ab5]8 [Ab5]8+                    [Ab5]8 [Ab5]8 [D5]8 [D5]8 [Ab5]8 [Ab5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [C5]8 [C5]8 [G5]8 [G5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [F5]4 []2+                    []2.+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [G5]8 [G5]8 [G4]8 [G4]8 [G5]8 [G5]8+                    [Ab5]8 [Ab5]8 [C5]8 [C5]8 [Ab5]8 [Ab5]8+                    [Ab5]8 [Ab5]8 [Eb5]8 [Eb5]8 [Ab5]8 [Ab5]8+                    [Ab5]8 [Ab5]8 [Ab4]8 [Ab4]8 [Ab5]8 [Ab5]8+                    [Ab5]4 [G5]2+                    [Ab5]2.]]}]}+          Pitched Instrument_and_staves {+            instrument_name = "Violin II"+            short_instrument_name = "vn II"+            midi_instrument = 40+            velocity = 127+            staves = [+              Stave {+                clef = Pitched_clef {clef_name = Treble transposition = 0}+                tracks = [+                  [+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [E5]8 [E5]8 [E4]8 [E4]8 [E5]8 [E5]8+                    [G5]8 [G5]8 [E4]8 [E4]8 [G5]8 [G5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [F5]8 [F5]8 [Bb4]8 [Bb4]8 [D5]8 [D5]8+                    [Db5]8 [Db5]8 [Eb4]8 [Eb4]8 [Db5]8 [Db5]8+                    [Eb5]8 [Eb5]8 [Ab4]8 [Ab4]8 [C5]8 [C5]8+                    [C5]8 [C5]8 [Db4]8 [Db4]8 [C5]8 [C5]8+                    [Bb4]8 [Bb4]8 [Db4]8 [Db4]8 [Bb4]8 [Bb4]8+                    [E5]8 [E5]8 [E4]8 [E4]8 [E5]8 [E5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [F5]8 [F5]8 [E4]8 [E4]8 [E5]8 [E5]8+                    [F5]4 []2+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    [C5]8 [C5]8 [C4]8 [C4]8 [C5]8 [C5]8+                    [B4]8 [B4]8 [B3]8 [B3]8 [B4]8 [B4]8+                    [D5]8 [D5]8 [G4]8 [G4]8 [D5]8 [D5]8+                    [C5]4 []2+                    [Eb5]8 [Eb5]8 [Eb4]8 [Eb4]8 [Eb5]8 [Eb5]8+                    [C5]8 [C5]8 [Ab4]8 [Ab4]8 [C5]8 [C5]8+                    [C5]8 [C5]8 [Ab4]8 [Ab4]8 [C5]8 [C5]8+                    [Db5]8 [Db5]8 [Bb3]8 [Bb3]8 [Db5]8 [Db5]8+                    [Db5]8 [Db5]8 [Bb4]8 [Bb4]8 [Db5]8 [Db5]8+                    [Db5]8 [Db5]8 [Bb4]8 [Bb4]8 [Db5]8 [Db5]8+                    [E5]8 [E5]8 [C4]8 [C4]8 [E5]8 [E5]8+                    [E5]8 [E5]8 [C4]8 [C4]8 [E5]8 [E5]8+                    [E5]8 [E5]8 [C4]8 [C4]8 [E5]8 [E5]8+                    [E5]8 [E5]8 [C4]8 [C4]8 [E5]8 [E5]8+                    [C5]4 []2+                    []2.+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [E5]8 [E5]8 [C4]8 [C4]8 [E5]8 [E5]8+                    [C5]4 []2+                    []2.+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [E5]8 [E5]8 [E4]8 [E4]8 [E5]8 [E5]8+                    [G5]8 [G5]8 [E4]8 [E4]8 [G5]8 [G5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [F5]8 [F5]8 [Bb4]8 [Bb4]8 [D5]8 [D5]8+                    [Db5]8 [Db5]8 [Eb4]8 [Eb4]8 [Db5]8 [Db5]8+                    [Eb5]8 [Eb5]8 [Ab4]8 [Ab4]8 [C5]8 [C5]8+                    [C5]8 [C5]8 [Db4]8 [Db4]8 [C5]8 [C5]8+                    [Bb4]8 [Bb4]8 [Db4]8 [Db4]8 [Bb4]8 [Bb4]8+                    [E5]8 [E5]8 [E4]8 [E4]8 [E5]8 [E5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [F5]8 [F5]8 [E4]8 [E4]8 [E5]8 [E5]8+                    [F5]4 []2+                    []2.+                    []2.+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [F5]8 [F5]8 [Bb4]8 [Bb4]8 [D5]8 [D5]8+                    [Db5]8 [Db5]8 [Eb4]8 [Eb4]8 [Db5]8 [Db5]8+                    [Eb5]8 [Eb5]8 [Ab4]8 [Ab4]8 [C5]8 [C5]8+                    [C5]8 [C5]8 [C4]8 [C4]8 [C5]8 [C5]8+                    [D5]4 []2+                    []2.+                    [Eb5]8 [Eb5]8 [Eb4]8 [Eb4]8 [Eb5]8 [Eb5]8+                    [E5]8 [E5]8 [E4]8 [E4]8 [E5]8 [E5]8+                    [E5]8 [E5]8 [E4]8 [E4]8 [E5]8 [E5]8+                    [E5]8 [E5]8 [E4]8 [E4]8 [E5]8 [E5]8+                    [F5]8 [F5]8 [Ab4]8 [Ab4]8 [F5]8 [F5]8+                    [Eb5]8 [Eb5]8 [C5]8 [C5]8 [Eb5]8 [Eb5]8+                    [F5]8 [F5]8 [F4]8 [F4]8 [F5]8 [F5]8+                    [F5]8 [C5]8 [Bb4]2+                    [C5]2.]]}]}+          Pitched Instrument_and_staves {+            instrument_name = "Viola"+            short_instrument_name = "va"+            midi_instrument = 41+            velocity = 127+            staves = [+              Stave {+                clef = Pitched_clef {clef_name = Alto transposition = 0}+                tracks = [+                  [+                    [C4]4 [C4]4 [C4]4+                    [C4]4 [C4]4 [C4]4+                    [C4]4 [C4]4 [C4]4+                    [C4]4 [C4]4 [C4]4+                    [D4]4 [D4]4 [Bb3]4+                    [Bb3]4 [Bb3]4 [Bb3]4+                    [C4]4 [C4]4 [Ab3]4+                    [Ab3]4 [Ab3]4 [Ab3]4+                    [Bb3]4 [Bb3]4 [Bb3]4+                    [C4]4 [C4]4 [C4]4+                    [C4]4 [C4]4 [C4]4+                    [Db4]4 [C4]4 [C4]4+                    [C4]4 []2+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    [G3]4 [G3]4 [G3]4+                    [G3]4 [G3]4 [G3]4+                    [G3]4 [G3]4 [G3]4+                    [G3]4 []2+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    [C4]4 [C4]4 [C4]4+                    [C4]4 [C4]4 [C4]4+                    [C4]4 [C4]4 [C4]4+                    [C4]4 [C4]4 [C4]4+                    [D4]4 [D4]4 [Bb3]4+                    [Bb3]4 [Bb3]4 [Bb3]4+                    [C4]4 [C4]4 [Ab3]4+                    [Ab3]4 [Ab3]4 [Ab3]4+                    [Bb3]4 [Bb3]4 [Bb3]4+                    [Bb3]4 [Bb3]4 [Bb3]4+                    [Ab3]4 [C4]4 [C4]4+                    [Db4]4 [C4]4 [C4]4+                    [C4]4 []2+                    []2.+                    []2.+                    [F4]4 [F4]4 [F4]4+                    [Bb3]4 [Bb3]4 [Bb3]4+                    [Eb4]4 [Eb4]4 [Eb4]4+                    [Ab3]4 [Ab3]4 [Ab3]4+                    [Ab3]4 [Ab3]4 [Ab3]4+                    [Bb3]4 []2+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.]]}]}]+        [+          Pitched Instrument_and_staves {+            instrument_name = "Voice"+            short_instrument_name = "v"+            midi_instrument = 52+            velocity = 127+            staves = [+              Stave {+                clef = Pitched_clef {clef_name = Treble transposition = 0}+                tracks = [+                  [+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []4 [F4]4 [G4]8 [Ab4]8+                    [G4]8. [F4]16 [E4]8. [D4]16 [C4]4+                    []4 [G4]4 [Ab4]8 [Bb4]8+                    [Ab4]8. [G4]16 [F4]2+                    []4 [Ab4]4. [Bb4]8+                    [G4]4. [Ab4]16 [G4]16 [C5]8 [Ab4]8+                    [F4]4. [G4]16 [F4]16 [Bb4]8 [G4]8+                    [E4]8. [F4]16 [G4]8. [Ab4]16 [Bb4]8. [G4]16+                    [Ab4]8 [G4]8 [F4]4 []4+                    [Ab4]8 [G4]8 [F4]8 [Eb4]8 [D4]8 [Eb4]16 [C4]16+                    [B4]4. [A4]16 [G4]16 [C5]8 [Bb4]16 [Ab4]16+                    [G4]8 [F4]16 [Eb4]16 [D4]2+                    [C4]2 []4+                    []2.+                    []2.+                    [C4]8. [D4]16 [Eb4]8. [F4]16 [G4]8 [A4]16 [B4]16+                    [C5]8. [C4]16 [C4]4 []4+                    [Ab4]4 [+8 [C5]8 [Bb4]8] [[Ab4]8 [Bb4]8 [G4]8]+                    [F4]4 [+8 [Ab4]8 [G4]8] [[F4]8 [G4]8 [Eb4]8]+                    [Db4]8. [C4]16 [Bb3]4 []4+                    [Bb4]4 [+8 [Db5]8 [C5]8] [[Bb4]8 [C5]8 [Ab4]8]+                    [G4]4 [+8 [Bb4]8 [Ab4]8] [[G4]8 [Ab4]8 [F4]8]+                    [E4]8. [D4]16 [C4]4 []4+                    [C4]8. [D4]16 [E4]8. [F4]16 [G4]8 [Ab4]16 [Bb4]16+                    [C4]4 [+8 [G4]8 [Ab4]8] [[Bb4]8 [Ab4]8 [G4]8]+                    [C4]4 [+8 [G4]8 [Ab4]8] [[Bb4]8 [Ab4]8 [G4]8]+                    [Ab4]4. [G4]8 [[F4]8 [G4]8 [F4]8]+                    [G4]8. [D4]16 [E4]2+                    [F4]2 []4+                    [Bb4]4 [Ab4]4 [G4]4+                    [Ab4]4 [+8 [F4]8 [G4]8] [[Ab4]8 [Bb4]8 [C5]8]+                    [Db5]8 [F4]8 [E4]2+                    [F4]2 []4+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []2.+                    []4 [C4]4 [G4]4+                    [Eb4]4. [D4]8 [C4]4+                    []4 [Ab4]4 [Ab4]4+                    [Ab4]4. [G4]8 [F4]4+                    []4 [G4]4 [G4]4+                    [G4]4. [F4]8 [Eb4]4+                    []4 [F4]4 [Eb4]8 [F4]8+                    [D4]4. [Ab4]8 [G4]8 [F4]8+                    [G4]8 [Ab4]8 [F4]2+                    [Eb4]2 []4+                    []4 [G4]4 [Bb4]4+                    [E4]4. [F4]8 [E4]4+                    [Bb4]4 [G4]4 [F4]8 [E4]8+                    [F4]4. [G4]8 [F4]4+                    []4 [Ab4]8 [Bb4]8 [C5]8 [Ab4]8+                    [F4]4. [G4]8 [Ab4]8 [F4]8+                    [C5]8 [Ab4]8 [G4]4. [Ab4]8+                    [Ab4]2.]]}]}]+        [+          Pitched Instrument_and_staves {+            instrument_name = "Continuo"+            short_instrument_name = "bc"+            midi_instrument = 6+            velocity = 127+            staves = [+              Stave {+                clef = Pitched_clef {clef_name = Bass transposition = 0}+                tracks = [+                  [+                    [F3]4 [F3]4 [F3]4+                    [C3]4 [C3]4 [C3]4+                    [C3]4 [C3]4 [C3]4+                    [F3]4 [F3]4 [F3]4+                    [Bb2]4 [Bb2]4 [Bb2]4+                    [Eb3]4 [Eb3]4 [Eb3]4+                    [Ab2]4 [Ab2]4 [Ab2]4+                    [Db3]4 [Db3]4 [Db3]4+                    [Db3]4 [Db3]4 [Db3]4+                    [C3]4 [C3]4 [C3]4+                    [F2]4 [F2]4 [F2]4+                    [Bb2]4 [C3]4 [C3]4+                    [F2]4 []2+                    []2.+                    [C4]8 [C4]8 [C3]8 [C3]8 [C4]8 [C4]8+                    [F3]8 [F3]8 [F2]8 [F2]8 [F3]8 [F3]8+                    [F3]8 [F3]8 [F2]8 [F2]8 [F3]8 [F3]8+                    [F3]8 [F3]8 [Eb2]8 [Eb2]8 [Eb3]8 [Eb3]8+                    [Eb3]8 [Eb3]8 [Db2]8 [Db2]8 [Db3]8 [Db3]8+                    [C3]8 [C3]8 [C2]8 [C2]8 [C3]8 [C3]8+                    [F3]8 [F3]8 [F2]8 [F2]8 [F3]8 [F3]8+                    [F3]8 [F3]8 [F2]8 [F2]8 [F3]8 [F3]8+                    [F3]8 [F3]8 [F2]8 [F3]8 [Eb3]8 [Eb3]8+                    [F3]8 [F3]8 [G3]8 [G3]8 [G2]8 [G2]8+                    [C3]4 [C3]4 [C3]4+                    [G3]4 [G3]4 [G3]4+                    [B2]4 [B2]4 [B2]4+                    [C3]4 []2+                    [C3]4 [C3]4 [C3]4+                    [Ab2]4 [Ab2]4 [Ab2]4+                    [Ab2]4 [Ab2]4 [Ab2]4+                    [Bb2]4 [Bb2]4 [Bb2]4+                    [Bb2]4 [Bb2]4 [Bb2]4+                    [Bb2]4 [Bb2]4 [Bb2]4+                    [C3]4 [C3]4 [C3]4+                    [C3]4 [C3]4 [C3]4+                    [C3]4 [C3]4 [C3]4+                    [C3]4 [C3]4 [C3]4+                    [F2]4 []4 [Ab2]4+                    [Bb2]4 [C3]4 [C2]4+                    [F2]4 [F3]4 [F3]4+                    [C3]4 [C3]4 [C3]4+                    [F3]4 [F3]4 [F3]4+                    [Bb3]4 [C4]4 [C3]4+                    [F2]4 [F3]4 [F3]4+                    [C3]4 [C3]4 [C3]4+                    [C3]4 [C3]4 [C3]4+                    [F3]4 [F3]4 [F3]4+                    [Bb2]4 [Bb2]4 [Bb2]4+                    [Eb3]4 [Eb3]4 [Eb3]4+                    [Ab2]4 [Ab2]4 [Ab2]4+                    [Db3]4 [Db3]4 [Db3]4+                    [Db3]4 [Db3]4 [Db3]4+                    [C3]4 [C3]4 [C3]4+                    [F2]4 [F2]4 [F2]4+                    [Bb2]4 [C3]4 [C2]4+                    [F2]4 []2+                    [C4]8 [C4]8 [C3]8 [C3]8 [C4]8 [C4]8+                    [C4]8 [C4]8 [C3]8 [C3]8 [C4]8 [C4]8+                    [F3]4 []2+                    []2.+                    []2.+                    []2.+                    []2.+                    [Bb2]4 [Bb2]4 [Bb2]4+                    [Eb3]4 [Bb3]4 [Bb2]4+                    [Eb3]4 [Eb3]4 [Eb3]4+                    [Db3]4 [Db3]4 [Db3]4+                    [Db3]4 [Db3]4 [Db3]4+                    [Db3]4 [Db3]4 [Db3]4+                    [Ab2]4 [Ab2]4 [Ab2]4+                    [C3]4 [C3]4 [C3]4+                    [Db3]4 [Db3]4 [Db3]4+                    [Db3]4 [Eb3]4 [Eb2]4+                    [Ab2]2.]]}]}]]}]}