-- Copyright 2013 Evan Laforge -- This program is distributed under the terms of the GNU General Public -- License 3.0, see COPYING or http://www.gnu.org/licenses/gpl-3.0.txt -- | Define a few inhabitants of Environ which are used by the built-in set of -- calls. Expected types are in 'Derive.Env.hardcoded_types'. module Derive.EnvKey where import Data.Text (Text) -- | Symbols to look up a val in the 'ValMap'. type Key = Text -- * directly supported by core derivers -- | VList: arguments for a 'Derive.Call.Tags.requires_postproc' call. -- Also see 'Derive.Call.Post.make_delayed'. args :: Key args :: Text args = Text "args" -- | VAttributes: Default set of attrs. attributes :: Key attributes :: Text attributes = Text "attr" -- | ScoreTime: the block deriver sets it to the ScoreTime end of the block. -- Blocks always start at zero, but this is the only way for a call to know if -- an event is at the end of the block. block_end :: Key block_end :: Text block_end = Text "block-end" -- | VStr: Set to the control that is being derived, inside of a control -- track. control :: Key control :: Text control = Text "control" -- | VStr: Instrument in scope. instrument :: Key instrument :: Text instrument = Text "inst" -- | VStr: Many scales use this for scale-specific configuration. -- Some scales conceptually have a separate key and mode, e.g. C minor, in -- which case they're combined into @key@. key :: Key key :: Text key = Text "key" -- | VNum (ScoreTime): End time of the note event. This is set when evaluating -- note events so that inverted control tracks know when their parent event -- ends. note_end :: Key note_end :: Text note_end = Text "note-end" -- | This is just like 'note_end', except, you know, the other end. note_start :: Key note_start :: Text note_start = Text "note-start" -- | VStr: Set along with 'control' to the 'Derive.Derive.Merge' function -- which will be used for this control track. Calls can use this to subvert -- the merge function and emit an absolute value. -- -- Values are @compose@ for tempo tracks, @set@, or any of the names from -- 'Derive.Derive.ControlOp'. merge :: Key merge :: Text merge = Text "merge" -- | VStr: Default scale, used by pitch tracks with a @*@ title. scale :: Key scale :: Text scale = Text "scale" -- | VNum: Random seed used by randomization functions. Can be explicitly -- initialized to capture a certain \"random\" variation. -- -- This is rounded to an integer, so only integral values make sense. seed :: Key seed :: Text seed = Text "seed" -- | VNum: Sampling rate used by signal interpolators. srate :: Key srate :: Text srate = Text "srate" -- | VNum: Set the default tempo, overriding 'Ui.State.default_tempo'. This -- only applies if there is no toplevel tempo track, and generally only has an -- effect if the block is played as a toplevel block since it's a constant -- tempo. -- -- Previously I would directly set the tempo warp in the equal call, but tempo -- setting has to be at the toplevel tempo track for it to interact properly -- with block call stretching. tempo :: Key tempo :: Text tempo = Text "tempo" -- | VNum: this is the count of the tracks with the same instrument, starting -- from 0 on the left. So three tracks named @>pno@ would be 0, 1, and 2, -- respectively. Used with "Derive.Call.InferTrackVoice". track_voice :: Key track_voice :: Text track_voice = Text "track-voice" -- * internal -- | RealTime: suppress other notes until this time, inclusive. Only events -- without a suppress-until will be retained. Applied by @infer-duration@, see -- "Derive.Call.Post.Move". suppress_until :: Key suppress_until :: Text suppress_until = Text "suppress-until" -- | VNum: This is a bit of a hack for the dynamic to velocity conversion in -- "Perform.Midi.Convert". The default note deriver stashes the control -- function output here so it can use it for note on velocity. Otherwise I -- couldn't use ControlFunctions (e.g. randomization) for note on velocity -- because control functions don't exist after MIDI conversion. -- -- Details in NOTE [EnvKey.dynamic_val]. dynamic_val :: Key dynamic_val :: Text dynamic_val = Text "dyn-val" -- | Like 'dynamic_val', but for the release velocity. Set from -- 'Derive.Controls.release_velocity'. release_val :: Key release_val :: Text release_val = Text "release-val" -- | VNum: Set from 'Derive.Controls.attack_velocity' in the same way as -- 'release_val'. attack_val :: Key attack_val :: Text attack_val = Text "attack-val" -- | RealTime: This stores the RealTime sum of 'Derive.Controls.start_s' and -- 'Derive.Controls.start_t', and is later applied by the @apply-start-offset@ -- postproc. start_offset_val :: Key start_offset_val :: Text start_offset_val = Text "start-offset-val" -- | Bool: Control tracks normally range from 0 to 1, or -1 to 1. This is set -- for control tracks which range >0. That's just the tempo track for now. control_gt_0 :: Key control_gt_0 :: Text control_gt_0 = Text "control-gt-0" -- * supported by not so core derivers -- | VNotePitch or VNum (NN): The top of the instrument's range. -- -- It's a VNotePitch for instruments that are tied to a particular family of -- scales, and have an upper note that is independent of any particular -- frequency. For instance, a kantilan's top note will have a different -- NoteNumber depending on its scale, or even within a single scale, depending -- if it is pengumbang or pengisep. -- -- For instruments with less complicated scale requirements, NoteNumber is -- simpler. instrument_top :: Key instrument_top :: Text instrument_top = Text "inst-top" instrument_bottom :: Key instrument_bottom :: Text instrument_bottom = Text "inst-bottom" -- | List VPitch: tuning of open strings for this instrument. The pitches -- should be probably absolute NNs, not in any scale, so they work regardless -- of which scale you happen to be in. -- -- TODO maybe it should be VNotePitch as with 'instrument_top'? open_strings :: Key open_strings :: Text open_strings = Text "open-strings" -- | VPitch: Select a string to play on. Presumably it should be from one of -- the 'open_strings'. TODO currently I address strings by pitch. It's -- convenient because general purpose string calls work with pitches, but -- will be ambiguous for instruments where multiple strings have the same -- pitch. string :: Key string :: Text string = Text "string" -- | VStr: Kind of tuning for the scale in scope. The meaning is dependent -- on the scale, e.g. ngumbang ngisep for Balinese scales. tuning :: Key tuning :: Text tuning = Text "tuning" -- | VNum: Separate notes into different voices. This is used by integrate to -- put them on their own tracks, and by the lilypond backend to split them into -- their own voices. Should be an integer from 1 to 4. voice :: Key voice :: Text voice = Text "v" -- | VStr: @right@, @r@, @left@, or @l@. Used by the lilypond backend, and -- also by any call that relies on an instrument's parts being divided by hand. hand :: Key hand :: Text hand = Text "hand" -- | VNum: hold the start of a call for a certain amount of ScoreTime or -- RealTime. hold :: Key hold :: Text hold = Text "hold" -- * im-specific -- | VStr: Used by the im backend to put into 'Synth.Shared.Note.element'. element :: Key element :: Text element = Text "element" -- * values -- | Instrument role. polos, sangsih :: Text polos :: Text polos = Text "polos" sangsih :: Text sangsih = Text "sangsih" {- NOTE [EnvKey.dynamic_val] I originally intended to handle 'Instrument.Pressure' with a note call override. But it turns out that Pressure is also used by Cmd, so I still need it. But to get velocity from control functions I have to include the ControlValMap in Score.Event. I waffled for a long time about whether it was better to handle in the note call or in conversion, and initially favored the note call because it feels like complexity should go in Derive, which is configurable, and not in Convert. But it turns out doing dyn mapping in Derive then breaks integration, so I'd have to undo it for integration. That made me think that this is really a low level MIDI detail and perhaps it's best handled by Convert after all. One problem with doing the dyn conversion here is that for a control function on dyn to have any effect I need the value from the control function, which means I need a scalar value. But by the time I get here the control functions are already gone. The note call can't know which of the dyn signal or control function is wanted, because that decision is made here. One solution was to put a ControlValMap in Score.Event so they get here, but that means that anything that modifies controls also has to remember to modify the ControlValMap. I can do that by updating 'Score.modify_control', but it seems like overkill when all I really want is to communicate the dyn value. So instead I stash the control function value in 'Controls.dynamic_function'. Unfortunately this brings it's own complications since now I need to remember to modify it when I modify an event's dynamic, and filter it out of integration so it doesn't create a track for it. So neither way is very satisfying, but at least this way doesn't require a whole new field in Score.Event. Perhaps I'll come up with something better someday. -}