packages feed

Aoide-2.0.0.0: Composition/Write.hs

{-|
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)