-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Programmatically edit MIDI event streams via ALSA -- -- MIDI is the Musical Instrument Digital Interface, ALSA is the Advanced -- Linux Sound Architecture. This package allows to manipulate a sequence -- of MIDI events via ALSA. It is intended to be plugged as a playing -- assistant between a MIDI input device (e.g. a keyboard or a controller -- bank) and a MIDI controlled synthesizer (e.g. a software synthesizer -- or an external synthesizer). For software synthesizers see the Haskell -- packages synthesizer-alsa, synthesizer-llvm, -- hsc3, YampaSynth or the C packages -- fluidsynth and Timidity. Applications include: -- Remapping of channels, controller, instruments, keys, Keyboard -- splitting, Conversion from notes to controllers, Latch mode, Convert -- parallel chords to serial patterns, Automated change of MIDI -- controllers, Echo simulation. It is intended that you write programs -- for MIDI stream manipulation. It is not intended to provide an -- executable program with all the functionality available in a custom -- programming interface. It is most fun to play with the stream editors -- in GHCi. However we provide an example program that demonstrates -- various effects. @package streamed @version 0.1 module Sound.MIDI.ALSA.Common data Handle Handle :: T DuplexMode -> T -> T -> T -> T -> Handle sequ :: Handle -> T DuplexMode client :: Handle -> T portIn :: Handle -> T portOut :: Handle -> T queue :: Handle -> T init :: IO Handle exit :: Handle -> IO () with :: ReaderT Handle IO a -> IO a -- | make ALSA set the time stamps in incoming events setTimeStamping :: ReaderT Handle IO () startQueue :: ReaderT Handle IO () connect :: String -> String -> ReaderT Handle IO () connectTimidity :: ReaderT Handle IO () connectLLVM :: ReaderT Handle IO () channel :: Int -> Channel pitch :: Int -> Pitch velocity :: Int -> Velocity controller :: Int -> Controller program :: Int -> Program -- | The Time types are used instead of floating point types, -- because the latter ones caused unpredictable 'negative number' errors. -- The denominator must always be a power of 10, this way we can prevent -- unlimited grow of denominators. type TimeAbs = Rational newtype Time Time :: Rational -> Time deconsTime :: Time -> Rational consTime :: String -> Rational -> Time incTime :: Time -> TimeAbs -> TimeAbs nano :: Num a => a makeEvent :: Handle -> TimeAbs -> Data -> T makeEcho :: Handle -> TimeAbs -> Custom -> T -- | The times are relative to the start time of the bundle and do not need -- to be ordered. type Bundle a = [(Time, a)] type EventDataBundle = Bundle Data singletonBundle :: a -> Bundle a timeFromStamp :: TimeStamp -> Time defaultTempoCtrl :: (Channel, Controller) -- | Transpose a note event by the given number of semitones. Non-note -- events are returned without modification. If by transposition a note -- leaves the range of representable MIDI notes, then we return Nothing. transpose :: Int -> Data -> Maybe Data setChannel :: Channel -> Data -> Data -- |
-- > replaceProgram [1,2,3,4] 5 [10,11,12,13] -- (True,[10,11,2,13]) --replaceProgram :: [Int32] -> Int32 -> [Int32] -> (Bool, [Int32]) programFromBanks :: [Int32] -> [Int32] -> Int32 -- | Interpret program changes as a kind of bank switches in order to -- increase the range of instruments that can be selected via a block of -- patch select buttons. -- -- programAsBanks ns divides the first sum ns -- instruments into sections of sizes ns!!0, ns!!1, .... Each -- program in those sections is interpreted as a bank in a hierarchy, -- where the lower program numbers are the least significant banks. -- Programs from sum ns on are passed through as they are. -- product ns is the number of instruments that you can address -- using this trick. In order to avoid overflow it should be less than -- 128. -- -- E.g. programAsBanks [n,m] interprets subsequent program -- changes to a (0<=a<n) and n+b -- (0<=b<m) as a program change to b*n+a. -- programAsBanks [8,8] allows to select 64 instruments by 16 -- program change buttons, whereas programAsBanks [8,4,4] allows -- to address the full range of MIDI 128 instruments with the same number -- of buttons. programsAsBanks :: [Int32] -> Data -> State [Int32] Data nextProgram :: Note -> State [Program] EventDataBundle -- | Before every note switch to another instrument according to a list of -- programs given as state of the State monad. I do not know how to -- handle multiple channels in a reasonable way. Currently I just switch -- the instrument independent from the channel, and send the program -- switch to the same channel as the beginning note. traversePrograms :: Data -> State [Program] EventDataBundle -- | This function extends traversePrograms. It reacts on external -- program changes by seeking an according program in the list. This way -- we can reset the pointer into the instrument list. However the search -- must be limited in order to prevent an infinite loop if we receive a -- program that is not contained in the list. traverseProgramsSeek :: Int -> Data -> State [Program] EventDataBundle reduceNoteVelocity :: Word8 -> Note -> Note delayAdd :: Word8 -> Time -> Data -> EventDataBundle simpleNote :: Channel -> Pitch -> Velocity -> Note type KeySet = Map (Pitch, Channel) Velocity type KeyQueue = [((Pitch, Channel), Velocity)] eventsFromKey :: Time -> ((Pitch, Channel), Velocity) -> EventDataBundle selectFromLimittedChord :: Int -> Time -> KeyQueue -> EventDataBundle -- | Generate notes according to the key set, where notes for negative and -- too large indices are padded with keys that are transposed by octaves. selectFromOctaveChord :: Int -> Time -> KeyQueue -> EventDataBundle selectFromChord :: Integer -> Time -> KeyQueue -> EventDataBundle selectFromChordRatio :: Double -> Time -> KeyQueue -> EventDataBundle increasePitch :: Int -> Pitch -> Maybe Pitch selectInversion :: Double -> Time -> KeyQueue -> EventDataBundle updateChord :: NoteEv -> Note -> KeySet -> KeySet controllerMatch :: Channel -> Controller -> Ctrl -> Bool updateDur :: Ctrl -> (Time, Time) -> Time type Selector i = i -> Time -> KeyQueue -> EventDataBundle type Pattern i = (Selector i, [i]) data IndexNote i IndexNote :: Int -> i -> IndexNote i item :: i -> Int -> IndexNote i type PatternMulti i = (Selector i, T Int [IndexNote i]) fraction :: RealFrac a => a -> a data SweepState SweepState :: Double -> Double -> Double -> Double -> SweepState sweepSpeed :: SweepState -> Double sweepDepth :: SweepState -> Double sweepCenter :: SweepState -> Double sweepPhase :: SweepState -> Double flipSeq :: Int -> [Int] cycleDown :: Int -> Pattern Int pingPong :: Int -> Pattern Int crossSum :: Int -> Pattern Int cycleUp :: Int -> Pattern Int cycleDownAuto :: Pattern Integer pingPongAuto :: Pattern Integer crossSumAuto :: Pattern Integer cycleUpAuto :: Pattern Integer binaryLegato :: PatternMulti Int binaryAccident :: PatternMulti Int binaryStaccato :: PatternMulti Int decomposePositional :: Integer -> Integer -> [Integer] cycleUpOctave :: Int -> Pattern Int randomInversions :: Pattern Double random :: Pattern Double cycleUpInversions :: Int -> Pattern Double inversions :: [Double] -> Pattern Double examplePatternMultiTempo0 :: T Int [IndexNote Int] examplePatternMultiTempo1 :: T Int [IndexNote Int] checkChannel :: (Channel -> Bool) -> (Data -> Bool) checkPitch :: (Pitch -> Bool) -> (Data -> Bool) checkController :: (Controller -> Bool) -> (Data -> Bool) checkProgram :: (Program -> Bool) -> (Data -> Bool) instance Show i => Show (IndexNote i) instance Eq i => Eq (IndexNote i) instance Ord i => Ord (IndexNote i) instance Show Time instance Eq Time instance Ord Time instance Num Time instance Fractional Time instance C Time instance Monoid Time module Sound.MIDI.ALSA.EventList ioToLazyList :: IO a -> IO [a] inputEventsCore :: ReaderT Handle IO [T] inputEvents :: ReaderT Handle IO (T Time Data) pairListFromRelativeEvents :: T Time a -> [(TimeAbs, a)] outputEvent :: TimeAbs -> Data -> ReaderT Handle IO () outputEvents :: T Time Data -> ReaderT Handle IO () -- | Sends (drain) each event individually since the events in the bundle -- might be created in a lazy manner. outputEventBundles :: T Time EventDataBundle -> ReaderT Handle IO () outputEventBundled :: T Time EventDataBundle -> ReaderT Handle IO () data Trigger a Regular :: a -> Trigger a Trigger :: Trigger a type EventDataTrigger = Bundle (Trigger Data) makeTriggerEvent :: Handle -> TimeAbs -> Trigger Data -> T makeTriggerEvents :: Handle -> TimeAbs -> EventDataTrigger -> [T] -- | This function distinguishes between events from portIn and events that -- are generated by us. Our generated events must also send an echo to -- the input port in order to break event_input and thus trigger -- their delivery. outputTriggerEvents :: T Time EventDataTrigger -> ReaderT Handle IO () mergeGenerated :: T Time (Bundle a) -> T Time (Bundle a) -> T Time (Bundle (Trigger a)) equidistantEvents :: Time -> [a] -> T Time a whirl :: T Time EventDataBundle mergeGeneratedAtoms :: (Time -> a) -> T Time a -> T Time a -> T Time a pattern :: Selector i -> [i] -> Time -> T Time Data -> T Time EventDataTrigger patternTempo :: Selector i -> [i] -> ((Channel, Controller), (Time, Time, Time)) -> T Time Data -> T Time EventDataTrigger -- | This allows more complex patterns including pauses, notes of different -- lengths and simultaneous notes. patternMultiTempo :: Selector i -> T Int [IndexNote i] -> ((Channel, Controller), (Time, Time, Time)) -> T Time Data -> T Time EventDataTrigger -- | Automatically changes the value of a MIDI controller every -- period seconds according to a periodic wave. The wave -- function is a mapping from the phase in [0,1) to a controller -- value in the range (-1,1). The generation of the wave is -- controlled by a speed controller (minSpeed and -- maxSpeed are in waves per second), the modulation depth and -- the center value. The center controller is also the one where we emit -- our wave. That is, when modulation depth is zero then this effect is -- almost the same as forwarding the controller without modification. The -- small difference is, that we emit a controller value at a regular -- pattern, whereas direct control would mean that only controller value -- changes are transfered. -- --
-- sweep channel -- period (speedCtrl, (minSpeed, maxSpeed)) depthCtrl centerCtrl -- (ctrlRange (-1,1) (sin . (2*pi*))) ---- -- We could use the nice Wave abstraction from the synthesizer package, -- but that's a heavy dependency because of multi-parameter type classes. sweep :: Channel -> Time -> (Controller, (Time, Time)) -> Controller -> Controller -> (Double -> Double) -> T Time Data -> T Time EventDataTrigger -- | The function maintains empty bundles in order to maintain laziness -- breaks. These breaks are import for later merging of the streams. filter :: (a -> Bool) -> State (T Time (Bundle a)) (T Time (Bundle a)) filterSimple :: (a -> Bool) -> T Time (Bundle a) -> T Time (Bundle a) merge :: T Time (Bundle a) -> T Time (Bundle a) -> T Time (Bundle a) process :: (T Time Data -> T Time EventDataTrigger) -> ReaderT Handle IO () processSimple :: (T Time Data -> T Time EventDataBundle) -> ReaderT Handle IO () runWhirl :: ReaderT Handle IO () runDelay :: ReaderT Handle IO () runKeyboardSplit :: ReaderT Handle IO () runKeyboardSplitLow :: ReaderT Handle IO () runKeyboardSplitHigh :: ReaderT Handle IO () runNote :: Channel -> Time -> Velocity -> Pitch -> ReaderT Handle IO () runKey :: Channel -> Bool -> Velocity -> Pitch -> ReaderT Handle IO () runController :: Channel -> Controller -> Int -> ReaderT Handle IO () runProgram :: Channel -> Program -> ReaderT Handle IO () -- |
-- runCyclePrograms (map VoiceMsg.toProgram [8..12]) --runCyclePrograms :: [Program] -> ReaderT Handle IO () -- |
-- runProgramsAsBanks [8,4,4] --runProgramsAsBanks :: [Int32] -> ReaderT Handle IO () -- |
-- runPattern 0.12 (cycleUp 4) --runPattern :: Time -> Pattern i -> ReaderT Handle IO () -- |
-- runPatternTempo 0.12 (cycleUp 4) ---- --
-- runPatternTempo 0.2 (selectFromOctaveChord, cycle [0,1,2,0,1,2,0,1]) --runPatternTempo :: Time -> Pattern i -> ReaderT Handle IO () -- |
-- runPatternMultiTempo 0.1 (selectFromLimittedChord, let pat = [item 0 1] ./ 1 /. [item 1 1] ./ 2 /. [item 1 1] ./ 1 /. [item 0 1] ./ 2 /. pat in 0 /. pat) --runPatternMultiTempo :: Time -> PatternMulti i -> ReaderT Handle IO () runFilterSweep :: ReaderT Handle IO () main :: IO () instance Traversable Trigger instance Foldable Trigger instance Functor Trigger module Sound.MIDI.ALSA.Causal -- | The returned event list must be finite. data T a b Cons :: (TimeAbs -> Either c a -> State s (Maybe b, T Time c)) -> s -> (T Time c) -> T a b map :: (a -> b) -> T a b mapMaybe :: (a -> Maybe b) -> T a b compose :: T b c -> T a b -> T a c -- | Run two stream processor in parallel. We cannot use the Arrow -- method &&& since we cannot the first -- method of the Arrow class. Consider first :: arrow a b -- -> arrow (c,a) (c,b) and a trigger where arrow a b -- generates an event of type b. How could we generate -- additionally an event of type c without having an input -- event? parallel :: Monoid b => T a b -> T a b -> T a b traverse :: s -> (a -> State s b) -> T a b process :: T Data EventDataBundle -> ReaderT Handle IO () transposeBundle :: Int -> T Data EventDataBundle transpose :: Int -> T Data Data delayAdd :: Word8 -> Time -> T Data EventDataBundle pattern :: (Selector i, [i]) -> Time -> T Data EventDataBundle updateChordDur :: (Channel, Controller) -> (Time, Time) -> Data -> State (Time, KeySet) (Maybe EventDataBundle, T time body) patternTempo :: (Selector i, [i]) -> ((Channel, Controller), (Time, Time, Time)) -> T Data EventDataBundle patternMultiTempo :: (Selector i, T Int [IndexNote i]) -> ((Channel, Controller), (Time, Time, Time)) -> T Data EventDataBundle updateSerialChord :: Int -> NoteEv -> Note -> KeyQueue -> KeyQueue updateSerialChordDur :: Int -> (Channel, Controller) -> (Time, Time) -> Data -> State (Time, KeyQueue) (Maybe EventDataBundle, T time body) patternSerialTempo :: Int -> (Selector i, [i]) -> ((Channel, Controller), (Time, Time, Time)) -> T Data EventDataBundle sweep :: Channel -> Time -> (Controller, (Time, Time)) -> Controller -> Controller -> (Double -> Double) -> T Data EventDataBundle partition :: (a -> Bool) -> T a (Maybe a, Maybe a) maybeIn :: T a b -> T (Maybe a) b guide :: Monoid b => (a -> Bool) -> T a b -> T a b -> T a b cyclePrograms :: [Program] -> T Data EventDataBundle -- |
-- cycleProgramsDefer t ---- -- After a note that triggers a program change, we won't change the -- program in the next t seconds. This is in order to allow -- chords being played and in order to skip accidentally played notes. cycleProgramsDefer :: Time -> [Program] -> T Data EventDataBundle main :: IO () instance Category T