Karya, built on Sun Nov 26 01:04:37 PST 2017 (patch 0a920b2bde70c0cbac8ee09d158064798b61bbe5)

Cmd.Cmd

Description

Core CmdT monad that cmds run in.

A Cmd is what user actions turn into. The main thing they do is edit Ui.State, or Cmd.State, but a special subset can also do IO actions like saving and loading files.

The Cmd monad has two kinds of exception: abort or throw. Abort means that the Cmd decided that it's not the proper Cmd for this Msg (keystroke, mouse movement, whatever) and another Cmd should get a crack at it. Throw means that the Cmd failed and there is nothing to be done but log an error. When an exception is thrown, the ui and cmd states are rolled back and midi output is discarded.

Cmds should be in the monad Cmd.M m => m ....

They have to be polymorphic because they run in both IO and Identity. IO because some cmds such saving and loading files require IO, and Identity because the majority of cmds don't. REPL cmds run in IO so they can load and save, and the result is that any cmd that wants to be used from both Identity cmds (bound to keystrokes) and the REPL must be polymorphic in the monad.

Previously I used M instead of Monad m => CmdT m ... to establish Functor, but post-AMP I don't need that any more. But to maintain consistency I'll keep using M.

Synopsis

# Documentation

type CmdL a = CmdT IO a Source #

Cmds used by the REPL, which all run in IO.

data Status Source #

Constructors

 Done Stop further cmd processing, "consuming" the Msg. Continue Continue processing, so another Cmd will have an opportunity to see the Msg. Quit Pack it up and go home. PlayMidi !PlayMidiArgs Hack to control import dependencies, see Cmd.PlayC. FloatingInput !FloatingInput Open a FloatingInput box.

Instances

 # MethodsshowsPrec :: Int -> Status -> ShowS #showList :: [Status] -> ShowS #

Combine two Statuses by keeping the one with higher priority.

Arguments for "Cmd.PlayC.play".

Mmc config, descriptive name, events, tempo func to display play position, optional time to repeat at.

Constructors

 PlayMidiArgs Fieldsplay_sync :: !(Maybe SyncConfig) play_name :: !TextDescription of what is being played for logging.play_midi :: !Midi.Perform.MidiEvents play_inv_tempo :: !(Maybe Transport.InverseTempoFunction) play_repeat_at :: !(Maybe RealTime.RealTime) play_im_end :: !(Maybe RealTime.RealTime)If there are im notes, this is the end of the last one. This is so the play monitor thread knows when im will be done.

Instances

 # MethodsshowList :: [PlayMidiArgs] -> ShowS #

Constructors

 FloatingOpen !ViewId !Types.TrackNum !ScoreTime !Text !(Int, Int) Open a new floating text input. View, track, pos, (select start, select end). FloatingInsert !Text Insert the given text into an already open edit box.

Instances

 # MethodsshowList :: [FloatingInput] -> ShowS #

type RunCmd cmd_m val_m a = Ui.State -> State -> CmdT cmd_m a -> val_m (Result a) Source #

Cmds can run in either Identity or IO, but are generally returned in IO, just to make things uniform.

type Result a = (State, [MidiThru], [Log.Msg], Either Ui.Error (a, Ui.State, [Update.CmdUpdate])) Source #

The result of running a Cmd.

run :: Monad m => a -> RunCmd m m a Source #

Run the given command in Identity, but return it in IO, just as a convenient way to have a uniform return type with run (provided it is run in IO).

lift_id :: M m => CmdId a -> m a Source #

Promote a CmdId to a generic cmd, which can also run as a CmdT IO. TODO: shouldn't it be possible to do this for free?

run_id :: Ui.State -> State -> CmdT Identity a -> Result (Maybe a) Source #

Run the Cmd in Identity, returning Nothing if it aborted.

run_val :: Log.LogMonad m => Ui.State -> State -> CmdT m a -> m (Either String (a, State, Ui.State)) Source #

Like run, but write logs, and discard MIDI thru and updates.

sequence_cmds :: M m => [a -> m Status] -> a -> m Status Source #

Run a set of Cmds as a single Cmd. The first one to return non-Continue will return. Cmds can use this to dispatch to other Cmds.

# CmdT and operations

newtype CmdT m a Source #

Constructors

 CmdT (CmdStack m a)

Instances

class (Log.LogMonad m, Ui.M m) => M m where Source #

Minimal complete definition

Methods

get :: m State Source #

put :: State -> m () Source #

write_midi :: Interface.Message -> m () Source #

Log some midi to send out. This is the midi thru mechanism. You can give it a timestamp, but it should be 0 for thru, which will cause it to go straight to the front of the queue. Use midi for normal midi thru.

abort :: m a Source #

An abort is an exception to get out of CmdT, but it's considered the same as returning Continue. It's so a command can back out if e.g. it's selected by the Keymap but has an additional prerequisite such as having an active block.

catch_abort :: m a -> m (Maybe a) Source #

Instances

 (Applicative.Applicative m, Monad m) => M (CmdT m) # Methodsput :: State -> CmdT m () Source #abort :: CmdT m a Source #catch_abort :: CmdT m a -> CmdT m (Maybe a) Source #

## exceptions

throw :: (CallStack.Stack, M m) => Text -> m a Source #

This is the same as Ui.throw, but it feels like things in Cmd may not always want to reuse State's exceptions, so they should call this one.

ignore_abort :: M m => m a -> m () Source #

Run a subcomputation that is allowed to abort.

rethrow_io :: IO a -> CmdT IO a Source #

Run an IO action, rethrowing any IO exception as a Cmd exception.

abort_unless :: M m => Maybe a -> m a Source #

Extract a Just value, or abort. Generally used to check for Cmd conditions that don't fit into a Keymap.

require :: (CallStack.Stack, M m) => Text -> Maybe a -> m a Source #

Throw an exception with the given msg on Nothing.

require_right :: (CallStack.Stack, M m) => (err -> Text) -> Either err a -> m a Source #

# State

data State Source #

App global state. Unlike Ui.State, this is not saved to disk. This is normally modified inside a CmdT, which is also a StateT, so it can also use the UI state functions. If an exception is thrown, both this state and the UI state will be rolled back.

This is kind of an unorganized wodge. The problem is that since state is all centralized in one place, every special snowflake Cmd that needs its own bit of state winds up getting its own little knob in here. On one hand, it's non-modular. On the other hand, it lets me keep an eye on it.

So far, most Cmds are pretty fundamental, so they more or less deserve their spots here. If it gets out of control, though, I'll have to either come up with a clever way of storing typed data where they can't collide, say by having a Cmd return a new Cmd and keeping the state trapped inside, or a less clever but simpler and easier way like Map Name Dynamic.

Constructors

 State Fieldsstate_config :: !Config state_save_file :: !(Maybe (Writable, SaveFile))If set, the current Ui.State was loaded from this file. This is so save can keep saving to the same file.state_saved :: !(Maybe Bool)Nothing means a state was just loaded, so it counts as saved even if it has changed. Just True means the state hasn't changed since being saved, and Just False means it has.state_ky_cache :: !(Maybe KyCache) state_derive_immediately :: !(Set BlockId)Omit the usual derive delay for these blocks, and trigger a derive. This is set by integration, which modifies a block in response to another block being derived. Blocks set to derive immediately are also considered to have block damage, if they didn't already. This is cleared after every cmd.state_history :: !HistoryHistory.state_history_config :: !HistoryConfig state_history_collect :: !HistoryCollect state_selection_history :: !SelectionHistory state_keys_down :: !(Map Modifier Modifier)Map of keys held down. Maintained by cmd_record_keys and accessed with keys_down. The key is the modifier stripped of extraneous info, like mousedown position. The value has complete info.state_focused_view :: !(Maybe ViewId)The block and track that have focus. Commands that address a particular block or track will address these.state_screens :: ![Rect.Rect]This contains a Rect for each screen.state_global_status :: !(Map Text Text)This is similar to view_status, except that it's global instead of per-view. So changes are logged with a special prefix so logview can catch them. Really I only need this map to suppress log spam.state_play :: !PlayState state_hooks :: !Hooks state_wdev_state :: !WriteDeviceStateMIDI state of WriteDevices.state_rdev_state :: !InputNote.ReadDeviceStateMIDI state of ReadDevices, including configuration like pitch bend range.state_edit :: !EditState state_repl_status :: !StatusThe status return for this Cmd. This is used only by the REPL, since non-REPL cmds simply return Status as their return value. REPL cmds can't do that because they commonly use the return value to return an interesting String back to the REPL.state_debug_ui_msgs :: !Bool

Instances

 # MethodsshowsPrec :: Int -> State -> ShowS #show :: State -> String #showList :: [State] -> ShowS #

data SaveFile Source #

Constructors

 SaveState !FilePath SaveRepo !GitTypes.Repo

Instances

 # Methods # MethodsshowList :: [SaveFile] -> ShowS #

data Writable Source #

Constructors

Instances

 # Methods # MethodsshowList :: [Writable] -> ShowS #

Directory of the save file.

data KyCache Source #

A loaded and parsed ky file, or an error string. This also has the files loaded and their timestamps, to detect when one has changed.

Constructors

 KyCache !(Either Text Library) !Fingerprint PermanentKy !Library This disables the cache mechanism. Tests use this to avoid having to set SaveFile.

Instances

 # MethodsshowList :: [KyCache] -> ShowS #

Keep track of loaded files and a fingerprint for their contents. This is used to detect when they should be reloaded.

Constructors

 Fingerprint ![FilePath] !Word.Word32

Instances

 # Methods # MethodsshowList :: [Fingerprint] -> ShowS # # Methodsmconcat :: [Fingerprint] -> Fingerprint #

Reset the parts of the State which are specific to a "session". This should be called whenever an entirely new state is loaded.

## Config

data Config Source #

Config type variables that change never or rarely. These mostly come from the App.StaticConfig.

Constructors

 Config Fieldsconfig_app_dir :: !FilePathApp root, initialized from Config.get_app_dir.config_midi_interface :: !Interface.Interface config_ky_paths :: ![FilePath]Search path for local definition files, from definition_path.config_rdev_map :: !(Map Midi.ReadDevice Midi.ReadDevice)Reroute MIDI inputs and outputs. These come from rdev_map and wdev_map and probably shouldn't be changed at runtime.config_wdev_map :: !(Map Midi.WriteDevice Midi.WriteDevice)WriteDevices can be score-specific, though, so another map is kept in Ui.State, which may override the one here.config_instrument_db :: !InstrumentDb config_library :: !LibraryLibrary of calls for the deriver.config_highlight_colors :: !(Map Color.Highlight Color.Color) config_im :: !Shared.Config.Config config_git_user :: !SaveGit.User

Instances

 # MethodsshowsPrec :: Int -> Config -> ShowS #showList :: [Config] -> ShowS #

Get a midi writer that takes the config_wdev_map into account.

Convert a relative path to place it in the app dir.

This was previously in Config, and configured via StaticConfig. But it turns out I don't really use StaticConfig. It has a name here just so I don't get references to Scale.All.lookup_scale everywhere.

## PlayState

data PlayState Source #

State concerning derivation, performance, and playing the performance.

Constructors

 PlayState Fieldsstate_play_control :: !(Maybe Transport.PlayControl)Transport control channel for the player, if one is running.state_performance :: !(Map BlockId Performance)When changes are made to a block, its performance will be recalculated in the background. When the Performance is forced, it will replace the existing performance in state_performance, if any. This means there will be a window in which the performance is out of date, but this is better than hanging the responder every time it touches an insufficiently lazy part of the performance.state_current_performance :: !(Map BlockId Performance)However, some cmds, like play, want the most up to date performance even if they have to wait for it. This map will be updated immediately.state_performance_threads :: !(Map BlockId Concurrent.ThreadId)Keep track of current thread working on each performance. If a new performance is needed before the old one is complete, it can be killed off.state_play_step :: !TimeStep.TimeStepSome play commands start playing from a short distance before the cursor.state_step :: !(Maybe StepState)Contain a StepState if step play is active. Managed in Cmd.StepPlay.state_play_multiplier :: RealTime.RealTimeGlobally speed up or slow down performance. It mutiplies the timestamps by the reciprocal of this amount, so 2 will play double speed, and 0.5 will play half speed.state_sync :: !(Maybe SyncConfig)If set, synchronize with a DAW when the selection is set, and on play and stop.

Instances

 # MethodsshowList :: [PlayState] -> ShowS #

data StepState Source #

Step play is a way of playing back the performance in non-realtime.

Constructors

 StepState Fieldsstep_view_id :: !ViewIdKeep track of the view step play was started in, so I know where to display the selection.step_tracknums :: [Types.TrackNum]If step play only applies to a few tracks, list them. If null, step play applies to all tracks.step_before :: ![(ScoreTime, State)]MIDI states before the step play position, in descending order.step_after :: ![(ScoreTime, State)]MIDI states after the step play position, in asceding order.

Instances

 # MethodsshowList :: [StepState] -> ShowS #

Configure synchronization. MMC is used to set the play position and MTC is used to start and stop playing.

MMC has start and stop msgs, but they seem useless, since they're sysexes, which are not delivered precisely.

Constructors

 SyncConfig Fieldssync_device :: !Midi.WriteDevice sync_device_id :: !Mmc.DeviceIdSend MMC to this device.sync_mtc :: !BoolIf true, send MTC on the sync_device. If this is set, MMC play and stop will be omitted, since the presence of MTC should be enough to get the DAW started, provided it's in external sync mode.DAWs tend to spend a long time synchronizing, presumably because hardware devices take time to spin up. That's unnecessary in software, so in Cubase you can set "lock frames" to 2, and in Reaper you can set "synchronize by seeking ahead" to 67ms.sync_frame_rate :: !Midi.FrameRate

Instances

 # MethodsshowList :: [SyncConfig] -> ShowS # # MethodsformatList :: [SyncConfig] -> Doc Source #

## hooks

newtype Hooks Source #

Hooks are Cmds that run after some event.

Constructors

 Hooks Fieldshooks_selection :: [[(ViewId, Maybe TrackSelection)] -> CmdId ()]Run when the selection changes.

Instances

 # MethodsshowsPrec :: Int -> Hooks -> ShowS #show :: Hooks -> String #showList :: [Hooks] -> ShowS # # Methodsmappend :: Hooks -> Hooks -> Hooks #mconcat :: [Hooks] -> Hooks #

Just a Sel.Selection annotated with its BlockId and TrackId. There's no deep reason for it, it just saves a bit of work for selection hooks.

## EditState

data EditState Source #

Editing state, modified in the course of editing.

Constructors

 EditState Fieldsstate_edit_mode :: !EditModeEdit mode enables various commands that write to tracks.state_floating_input :: !BoolTrue if the floating input edit is open.state_advance :: BoolWhether or not to advance the insertion point after a note is entered.state_chord :: BoolChord mode means the note is considered entered when all NoteOffs have been received. While a note is held down, the insertion point will move to the next note track with the same instrument so you can enter chords.When chord mode is off, the note is considered entered as soon as its NoteOn is received.state_record_velocity :: BoolTry to find or create a Score.c_dynamic track for to record InputNote.Input velocity, similar to how a pitch track is edited and created.state_kbd_entry :: !BoolUse the alphanumeric keys to enter notes in addition to midi input.state_time_step :: !TimeStep.TimeStepDefault time step for cursor movement.state_note_duration :: !TimeStep.TimeStepUsed for note duration. It's separate from state_time_step to allow for tracker-style note entry where newly entered notes extend to the next note or the end of the block.state_note_orientation :: !Event.OrientationIf this is Rewind, create notes with negative durations.state_note_text :: !TextNew notes get this text by default. This way, you can enter a series of notes with the same attributes, or whatever.state_kbd_entry_octave :: !Pitch.OctaveTranspose note entry on the keyboard by this many octaves. It's by octave instead of scale degree since scales may have different numbers of notes per octave.state_recorded_actions :: !RecordedActions state_instrument_attributes :: !(Map Score.Instrument Attrs.Attributes) state_edit_box :: !(Block.Box, Block.Box)See set_edit_box.

Instances

 # Methods # MethodsshowList :: [EditState] -> ShowS #

data EditMode Source #

These enable various commands to edit event text. What exactly val and method mean are dependent on the track.

Constructors

 NoEdit ValEdit MethodEdit

Instances

 # Methods # MethodsshowList :: [EditMode] -> ShowS # # MethodsformatList :: [EditMode] -> Doc Source #

data Action Source #

Repeat a recorded action.

Select event and duration and hit shift-1 to record InsertEvent. Text edits record ReplaceText, PrependText, or AppendText in the last action field (bound to .), which you can then save.

Constructors

 InsertEvent !(Maybe TrackTime) !Text If a duration is given, the event has that duration, otherwise it gets the current time step. ReplaceText !Text PrependText !Text AppendText !Text

Instances

 # Methods(==) :: Action -> Action -> Bool #(/=) :: Action -> Action -> Bool # # MethodsshowsPrec :: Int -> Action -> ShowS #showList :: [Action] -> ShowS # # MethodsformatList :: [Action] -> Doc Source #

### midi devices

Constructors

 WriteDeviceState Fieldswdev_note_addr :: !(Map InputNote.NoteId Patch.Addr)NoteId currently playing in each Addr. An Addr may have >1 NoteId.wdev_note_key :: !(Map InputNote.NoteId Midi.Key)The note id is not guaranteed to have any relationship to the key, so the MIDI NoteOff needs to know what key the MIDI NoteOn used.wdev_addr_serial :: !(Map Patch.Addr Serial)Map an addr to a number that increases when it's assigned a note. This is used along with wdev_serial to implement addr round-robin.wdev_serial :: !SerialNext serial number for wdev_addr_serial.wdev_last_note_id :: !(Maybe InputNote.NoteId)Last NoteId seen. This is needed to emit controls (rather than just mapping them from MIDI input) because otherwise there's no way to know to which note they should be assigned.wdev_pitch_track :: !(Map InputNote.NoteId (BlockId, Types.TrackNum))NoteIds being entered into which pitch tracks. When entering a chord, a PitchChange uses this to know which pitch track to update.wdev_addr_inst :: !(Map Patch.Addr Midi.Types.Patch)Remember the current patch of each addr. More than one patch or keyswitch can share the same addr, so I need to keep track which one is active to minimize switches.

Instances

 # Methods # MethodsshowList :: [WriteDeviceState] -> ShowS #

### instrument

The code part of an instrument, i.e. the calls and cmds it brings into scope.

This has to be in Cmd.Cmd for circular import reasons.

Constructors

 InstrumentCode Fieldsinst_calls :: !InstrumentCalls inst_postproc :: !InstrumentPostproc inst_cmds :: ![Msg.Msg -> CmdId Status] inst_thru :: !(Maybe ThruFunction)An optional specialized cmd to write MIDI thru. This is separate from inst_cmds so it can be run wherever the instrument needs MIDI thru, not just in the instrument's note track. This way custom thru works in the pitch track too.

Instances

 # MethodsshowList :: [InstrumentCode] -> ShowS # # Methods

type InstrumentPostproc = Score.Event -> (Score.Event, [Log.Msg]) Source #

Process each event before conversion. This is like a postproc call, but it can only map events 1:1 and you don't have to explicitly call it.

This can change the duration, but should not change Score.event_start, because the events are not resorted afterwards. Also, it's applied during conversion, so it only makes sense to modify Score.event_duration, event_controls, Score.event_pitch, and Score.event_environ. TODO so I could have it return just those? But then it has to return Maybe to not modify and needs a record type.

Instantiate Inst.Db with the code type. The only reason the Db has the type parameter is so I can define it in its own module without a circular import.

Like InstrumentDb.

### history

data History Source #

Ghosts of state past, present, and future.

Constructors

 History Fieldshist_past :: ![HistoryEntry] hist_present :: !HistoryEntryThe present is actually the immediate past. When you undo, the undo itself is actually in the future of the state you want to undo. So another way of looking at it is that you undo from the past to a point further in the past. But since you always require a "recent past" to exist, it's more convenient to break it out and call it the "present". Isn't time travel confusing?hist_future :: ![HistoryEntry] hist_last_cmd :: !(Maybe LastCmd)

Instances

 # MethodsshowList :: [History] -> ShowS # # MethodsformatList :: [History] -> Doc Source #

data LastCmd Source #

Record some information about the last cmd for the benefit of maintain_history.

Constructors

 UndoRedo This cmd set the state because it was an undo or redo. Otherwise undo and redo themselves would be recorded and multiple undo would be impossible! Load (Maybe GitTypes.Commit) [Text] This cmd set the state because of a load. This should reset all the history so I can start loading from the new state's history.

Instances

 # MethodsshowList :: [LastCmd] -> ShowS #

Constructors

 HistoryConfig Fieldshist_keep :: !IntKeep this many previous history entries in memory.hist_last_commit :: !(Maybe GitTypes.Commit)Checkpoints are saved relative to the state at the next checkpoint. So it's important to keep the commit of that checkpoint up to date, otherwise the state and the checkpoints will get out of sync.

Instances

 # MethodsshowList :: [HistoryConfig] -> ShowS # # Methods

Constructors

 HistoryCollect Fieldsstate_cmd_names :: ![Text]This is cleared after each cmd. A cmd can cons its name on, and the cmd is recorded with the (optional) set of names it returns. Hopefully each cmd has at least one name, since this makes the history more readable. There can be more than one name if the history records several cmds or if one cmd calls another.state_suppress_edit :: !(Maybe EditMode)Suppress history record until the EditMode changes from the given one. This is a bit of a hack so that every keystroke in a raw edit isn't recorded separately.state_suppressed :: !(Maybe SaveGitTypes.SaveHistory)The Git.Commit in the SaveHistory should definitely be Nothing.

Instances

 # MethodsshowList :: [HistoryCollect] -> ShowS # # Methods

Constructors

 HistoryEntry Fieldshist_state :: !Ui.State hist_updates :: ![Update.CmdUpdate]Since track event updates are not caught by diff but recorded by Ui.State, I have to save those too, or else an undo or redo will miss the event changes. TODO ugly, can I avoid this?If this HistoryEntry is in the past, these are the updates that took it to the future, not the updates emitted by the cmd itself. If the HistoryEntry is in the future, the updates take it to the past, which are the updated emitted by the cmd. So don't be confused if it looks like a HistoryEntry has the wrong updates.hist_names :: ![Text]Cmds involved creating this entry.hist_commit :: !(Maybe GitTypes.Commit)The Commit where this entry was saved. Nothing if the entry is unsaved.

Instances

 # MethodsshowList :: [HistoryEntry] -> ShowS # # MethodsformatList :: [HistoryEntry] -> Doc Source #

### SelectionHistory

Remember previous selections. This should be updated only by significant movements, so clicks and cmd-a, but not hjkl stuff.

Constructors

 SelectionHistory Fieldssel_past :: [(ViewId, Sel.Selection)] sel_future :: [(ViewId, Sel.Selection)]

Instances

 # Methods # MethodsshowList :: [SelectionHistory] -> ShowS # # Methods

### modifier

data Modifier Source #

Constructors

 KeyMod Key.Modifier MouseMod Types.MouseButton (Maybe (Types.TrackNum, UiMsg.Track)) Mouse button, and track it went down at, if any. The block is not recorded. You can't drag across blocks so you know any click must apply to the focused block. MidiMod Midi.Channel Midi.Key Only chan and key are stored. While it may be useful to map according to the device, this code doesn't know which devices are available. Block or track level handlers can query the device themselves.

Instances

 # Methods # Methods(<) :: Modifier -> Modifier -> Bool #(>) :: Modifier -> Modifier -> Bool # # Methods # MethodsshowList :: [Modifier] -> ShowS #

Take a modifier to its key in the modifier map which has extra info like mouse down position stripped.

## state access

gets :: M m => (State -> a) -> m a Source #

modify :: M m => (State -> State) -> m () Source #

get_screen :: M m => (Int, Int) -> m Rect.Rect Source #

Return the rect of the screen closest to the given point.

Clear all performances, which will cause them to be rederived. It's in IO because it wants to kill any threads still deriving.

TODO I'm not actually sure if this is safe. A stale DeriveComplete coming in should be ignored, right? Why not Ui.update_all_tracks?

keys_down :: M m => m (Map Modifier Modifier) Source #

Keys currently held down, as in state_keys_down.

focus :: Ui.M m => ViewId -> m () Source #

Request focus. state_focused_view will be updated once fltk reports the focus change.

lookup_focused_block :: M m => m (Maybe BlockId) Source #

In some circumstances I don't want to abort if there's no focused block.

Get the leftmost track covered by the insert selection, which is considered the "focused" track by convention.

set_view_status :: M m => ViewId -> (Int, Text) -> Maybe Text -> m () Source #

This just calls Ui.set_view_status, but all status setting should go through here so they can be uniformly filtered or logged or something.

set_global_status :: M m => Text -> Text -> m () Source #

Emit a special log msg that will cause log view to put this key and value in its status bar. A value of "" will cause logview to delete that key.

set_status :: M m => (Int, Text) -> Maybe Text -> m () Source #

Set a status variable on all views.

## lookup instrument

Constructors

 ResolvedInstrument Fields

Instances

 # MethodsshowList :: [ResolvedInstrument] -> ShowS # # Methods

data Backend Source #

Constructors

 Midi !Patch.Patch !Patch.Config Im !Im.Patch.Patch

Instances

 # MethodsshowList :: [Backend] -> ShowS # # MethodsformatList :: [Backend] -> Doc Source #

## lookup qualified name

lookup_qualified :: M m => InstTypes.Qualified -> m (Maybe Inst) Source #

Look up an instrument that might not be allocated.

## misc

derive_immediately :: M m => [BlockId] -> m () Source #

inflict_block_damage :: M m => BlockId -> m () Source #

Cause a block to rederive even if there haven't been any edits on it.

inflict_track_damage :: M m => BlockId -> TrackId -> m () Source #

Cause a track to rederive even if there haven't been any edits on it.

## EditState

set_edit_box :: M m => Block.Box -> Block.Box -> m () Source #

At the Ui level, the edit box is per-block, but I use it to indicate edit mode, which is global. So it gets stored in Cmd.State and must be synced with new blocks.

set_note_text :: M m => Text -> m () Source #

# util

name :: M m => Text -> m a -> m a Source #

Give a name to a Cmd. The name is applied when the cmd returns so the names come out in call order, and it doesn't incur overhead for cmds that abort.

suppress_history :: M m => EditMode -> Text -> m a -> m a Source #

Like name, but also set the state_suppress_edit. This will suppress history recording until the edit mode changes from the given one.

Log an event so that it can be clicked on in logview.

all_notes_off :: M m => m () Source #

Turn off all sounding notes, reset controls. TODO clear out WriteDeviceState?

This holds the final performance for a given block. It is used to actually play music, and poked and prodded in a separate thread to control its evaluation.

This is basically the same as Result. I could make them be the same, but Performance wasn't always the same and may not be the same in the future.

Unlike other records, the fields here are all lazy. This is because I need to put an unevaluated Performance into Cmd.state_current_performances, and then force the fields in a separate thread. Also I need to modify perf_damage without forcing any of the others.

Constructors

 Performance Fieldsperf_derive_cache :: Cache perf_events :: Vector Score.EventThis is the forced result of a derivation.perf_logs :: [Log.Msg]Logs from the derivation are written separately.perf_logs_written :: BoolThe logs are only written on the first play, to minimize error spam. So there's a flag which says whether these logs have been written or not. I don't clear the logs, so cache_stats can inspect them.perf_track_dynamic :: TrackDynamic perf_integrated :: [Integrated] perf_damage :: ScoreDamageScoreDamage is normally calculated automatically from the UI diff, but Cmds can also intentionally inflict damage to cause a rederive.perf_warps :: [TrackWarp.TrackWarp] perf_track_signals :: TrackSignals perf_ui_state :: Ui.StateThis is the score state at the time of the performance. It's needed to interpret perf_track_signals, because at the time signals are sent (in cmd_play_msg), the Ui.State may have unsynced changes.

Instances

 # MethodsshowList :: [Performance] -> ShowS # # MethodsformatList :: [Performance] -> Doc Source #