Karya, built on 2023-08-29T07:47:28 (patch 7a412d5d6ba4968ca4155ef276a062ccdeb9109a)
Safe HaskellSafe-Inferred

Cmd.Instrument.CUtil

Description

Functions for instrument cmds. This is called CUtil because there is also Derive.Instrument.DUtil and they are usually imported together.

I need a better name than "Util" for everything.

Synopsis

eval call

data Thru Source #

Select the flavor of thru to use when inserting an expression. This selects either expr_midi_thru or expr_im_thru.

Choosing manually is silly because the valid kind of thru depends on the patch type. It's just that due to history and wanting to avoid duplicated code, the Cmd and Derive code in here doesn't care about MIDI vs. im... except for thru.

insert_expr Source #

Arguments

:: Cmd.M m 
=> Thru

Evaluate the expression and emit MIDI thru.

-> Cmd.NoteEntryMap DeriveT.Expr 
-> Cmd.Handler m 

Create a custom kbd entry cmd that inserts tracklang expressions at the insertion point. Since this shadows the default note entry cmd, it has to handle thru on its own.

expr_midi_thru :: Cmd.M m => UiMsg.KbdState -> DeriveT.Expr -> m () Source #

Emit MIDI thru for an arbitrary expresison.

This is more accurate and principled than what the usual kbd entry cmds do, since it reuses the deriver and performer directly, while they recreate the performer in an ad-hoc way, e.g. in Cmd.MidiThru. However, this allows them to play chords and is thus more suitable for pitched instruments. Actually, what MidiThru recreates is the channel allocation part of the performer, ultimately becasue the performer's allocator doesn't work in real time. Still, perhaps it would be possible to integrate them better than I have.

expr_to_midi :: Cmd.M m => BlockId -> TrackId -> TrackTime -> DeriveT.Expr -> m [Midi.WriteMessage] Source #

Call a note call and return the MIDI msgs it produces.

keyswitch

keyswitches :: Cmd.M m => [(Char, Expr.Symbol, Midi.Key)] -> Cmd.Handler m Source #

Create a Cmd to set keyswitches.

This simply sets the note text for subsequent notes, and also configures the instrument to play in the given keyswitch.

TODO this just emits keyswitches for every addr and emits them redundantly. This is simpler but it would be more correct to use WriteDeviceState to emit them only when needed. However, it's more complicated because then I need a current attrs (Map Instrument Attrs) along with current note text, so MidiThru can use the attrs to find the keyswitch.

TODO if I can pull the current or previous note out of the derive then I could use that to play an example note. Wait until I have a "play current line" framework up for that.

drums

simple_drum :: Thru -> Maybe.Maybe ScoreT.Control -> [(Drums.Stroke, Midi.Key)] -> MidiInst.Patch -> MidiInst.Patch Source #

Create an unpitched drum instrument. This is an instrument with an enumeration of symbols and no pitch or duration. Each key maps to its own symbol.

code

drum_code :: Thru -> [(Drums.Stroke, CallConfig)] -> MidiInst.Code Source #

Construct code from drum strokes. This is both the deriver calls to interpret the stroke names, and the cmds to enter them.

drum_code_cmd :: [(Char, Expr.Symbol)] -> Thru -> [(Drums.Stroke, CallConfig)] -> MidiInst.Code Source #

drum_code, but with the opportunity to insert extra keys for insert_call. This is because insert_expr can't be stacked, since it consumes kbd entry keys it doesn't map, since it's confusing if it doesn't.

patch

drum_patch :: [(Drums.Stroke, Midi.Key)] -> MidiInst.Patch -> MidiInst.Patch Source #

Create a MIDI patch for the traditional kind of drum instrument where each different stroke is mapped to a single MIDI keys. pitched_drum_patch is the variant where each stroke has a range of keys.

im_drum_patch :: [Drums.Stroke] -> ImInst.Patch -> ImInst.Patch Source #

im is much simpler than MIDI and doesn't need all the keymap garbage. However, like drum_patch, this just sets patch config, the cmd and deriver code has to be added separately, see drum_code or drum_calls for that.

type PitchedStrokes = [(Drums.Stroke, KeyswitchRange)] Source #

Used with pitched_attribute_map for MIDI instruments that do the strategy of assigning pitch ranges to each drum stroke.

type KeyswitchRange = ([Patch.Keyswitch], Midi.Key, Midi.Key, Midi.Key) Source #

(keyswitch, low, high, root_pitch). The root pitch is the pitch at the bottom of the key range, and winds up in Patch.PitchedKeymap.

make_keymap Source #

Arguments

:: Maybe.Maybe Midi.Key

Keyswitches start here. If not given, this patch doesn't use keyswitches.

-> Midi.Key

notes start here

-> Midi.Key

each sound is mapped over this range

-> Midi.Key

the pitch of the bottom note of each range

-> [[Attrs.Attributes]] 
-> Map Attrs.Attributes KeyswitchRange 

Make a KeyswitchRange for each grouped Attributes set. Attributes in the same group get the same range and are differentiated by keyswitch.

make_keymap2 :: Maybe.Maybe Midi.Key -> Midi.Key -> Midi.Key -> Midi.Key -> Midi.Key -> [[Attrs.Attributes]] -> Map Attrs.Attributes KeyswitchRange Source #

This is like make_keymap, except with the arguments rearranged to more closely match the sample utils I use.

make_cc_keymap Source #

Arguments

:: Midi.Key

notes start here

-> Midi.Key

each sound is mapped over this range

-> Midi.Key

the pitch of the bottom note of each range

-> [[Attrs.Attributes]] 
-> Map Attrs.Attributes KeyswitchRange 

This is like make_keymap, except that attributes are differentiated by a Patch.ControlSwitch. CCs start at 102, and only groups of size >1 get a CC. Since each group is controlled by its own CC number, you can then select each variation independently. This means any set of variations can be played simultaneously, which is not true for keyswitches.

pitched_drum_patch :: PitchedStrokes -> MidiInst.Patch -> MidiInst.Patch Source #

Annotate a Patch with an Patch.AttributeMap from the given PitchedStrokes.

drum_pitched_strokes Source #

Arguments

:: [Drums.Stroke] 
-> Map Attrs.Attributes KeyswitchRange 
-> (PitchedStrokes, ([Drums.Stroke], [Attrs.Attributes]))

Also return the strokes with no mapping (so they can't be played), and keymap ranges with no corresponding strokes (so there is no call to play them).

Make PitchedStrokes by pairing each Drums.Stroke with its KeyswitchRange.

drum_calls :: [(Drums.Stroke, CallConfig)] -> [(Expr.Symbol, Generator Note)] Source #

Create a drum_call for each Drums.Stroke.

This should probably go in DUtil, but that would make it depend on Cmd.Instrument.Drums.

pitched_strokes :: [Attrs.Attributes] -> Pitch.NoteNumber -> Attrs.Attributes -> CallConfig Source #

For drum_calls. If Just, only strokes which are a superset of one of these move with the pitch, otherwise the stay at the given NoteNumber. If Nothing, all strokes move with the pitch.

data CallConfig Source #

Constructors

CallConfig 

Fields

drum_call :: CallConfig -> Expr.Symbol -> Attrs.Attributes -> Generator Note Source #

This is the common deriver call that all drums and drum-like instruments use at the bottom.

util

resolve_strokes Source #

Arguments

:: Signal.Y 
-> Map Attrs.Attributes KeyswitchRange 
-> [(Char, Expr.Symbol, Attrs.Attributes, Drums.Group)]

(key_binding, emits_text, call_attributes, stop_group)

-> (PitchedStrokes, [Text])

also return errors

Given a map describing how Attributes are mapped to the MIDI key range, take a key binding to a PitchedStrokes. The reason these are separate is that the map describes how a particular patch maps attributes, while the key binding describes the capabilities of the instrument itself.

If a mapping has Attrs.soft, it's looked up without the soft, but gets the given dynamic.