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

Cmd.InputNote

Description

Convert incoming MIDI (and other inputs) to the internal note representation. This has two purposes: note entry, and midi thru.

This module is, in a way, dual to Perform.Midi.Perform. It takes MIDI input to the internal Input representation and back again, while Perform takes the internal representation, in the form of Event, to MIDI output.

The overlapping part is that this module maps Controls to and from MIDI while Play uses Control. They use the same control names, though, so I can reuse code from Control.

One significant difference between Input and MIDI is that MIDI supports two levels of control addressing: note and channel, while Input can only represent note addressing through NoteId. MIDI controls almost all apply at the channel level, but of course these controls all apply at the note level. The result is that a MIDI control that on a keyboard affects the whole channel will only affect the last played note here. When the input is converted back to MIDI it may wind up sharing a channel anyway, at which point the control will go back to being channel global, but if the instrument has multiple channels, I try to distribute between them to keep note_ids on separate channels. This way, multiple channel emitting controls can be mapped to multiple channel using instruments.

This is basically a simplified version of the channel allocation algorithm in Perform.Midi.Perform. It's hard to reuse that algorithm directly because this one has to operate in realtime and can't see which controls the note is going to use.

Synopsis

Documentation

keyboard_velocity :: Signal.Y Source #

Since the ASCII keyboard isn't pressure sensitive, this is the default velocity. Hopefully it's strong but not so strong as to be hard on the ears.

type InputNn = GenericInput Pitch.NoteNumber Source #

An input with a plain NoteNumber pitch instead of a Pitch.Input.

data GenericInput pitch Source #

Constructors

NoteOn NoteId pitch Signal.Y

The Input and val (velocity) could be sent separately, but that would make converting this back into midi for thru harder.

NoteOff NoteId Signal.Y 
Control NoteId ScoreT.Control Signal.Y

Controls coming from MIDI are mapped to control names, since this is a superset of MIDI CC numbers, and may include non-MIDI as well. But for MidiThru to map back to a CC number, I need 1:1 mapping between ScoreT.Controls and CC numbers. This is what cc_to_control and control_to_cc provide.

PitchChange NoteId pitch

Pitch could also be a Control, but this way the pitch is typed.

Instances

Instances details
Show pitch => Show (GenericInput pitch) Source # 
Instance details

Defined in Cmd.InputNote

Methods

showsPrec :: Int -> GenericInput pitch -> ShowS #

show :: GenericInput pitch -> String #

showList :: [GenericInput pitch] -> ShowS #

Eq pitch => Eq (GenericInput pitch) Source # 
Instance details

Defined in Cmd.InputNote

Methods

(==) :: GenericInput pitch -> GenericInput pitch -> Bool #

(/=) :: GenericInput pitch -> GenericInput pitch -> Bool #

(Show pitch, Pretty pitch) => Pretty (GenericInput pitch) Source # 
Instance details

Defined in Cmd.InputNote

offset_note_id :: Int -> GenericInput x -> GenericInput x Source #

Modify the NodeId so that it won't collide with other NodeIds.

NoteIds are supposed to be unique for each Input. However, in practice they wind up being the MIDI NoteOn Midi.Key, for reasons described in NoteId. So if you want to emit MIDI thru for two notes with the same pitch (e.g. dispatch a single pitch to two instruments), you need to give them different NoteIds. This function multiplies them such that they won't collide.

TODO This is a grody hack. A better solution might be to make NoteId into a (Channel, Int) pair.

newtype NoteId Source #

In theory, NoteId is an arbitrary ID, but in practice it's the same as the initial note on Midi.Key. The reason is that pitch bend needs to know the original key so it knows what the pitch bend is relative to. I could store the original key separately, but it's convenient to put them both into NoteId, and I can't think of any instances where I'd want them to be different.

In addition, when a MIDI NoteOff comes in I have to know what NoteId it applies to. Since MIDI's NoteId is the key number, I have no choice but to use that.

Constructors

NoteId Int 

Instances

Instances details
Show NoteId Source # 
Instance details

Defined in Cmd.InputNote

Eq NoteId Source # 
Instance details

Defined in Cmd.InputNote

Methods

(==) :: NoteId -> NoteId -> Bool #

(/=) :: NoteId -> NoteId -> Bool #

Ord NoteId Source # 
Instance details

Defined in Cmd.InputNote

from midi

newtype ReadDeviceState Source #

Keep track of the state of each Midi.ReadDevice.

Instances

Instances details
Show ReadDeviceState Source # 
Instance details

Defined in Cmd.InputNote

Eq ReadDeviceState Source # 
Instance details

Defined in Cmd.InputNote

data ControlState Source #

The state of one Midi.ReadDevice.

Instances

Instances details
Show ControlState Source # 
Instance details

Defined in Cmd.InputNote

Eq ControlState Source # 
Instance details

Defined in Cmd.InputNote

from ascii

from_ascii :: Bool -> Pitch.Pitch -> Input Source #

Create an Input from an ascii keyboard Pitch.

to midi