live-sequencer 0.0.5.2 → 0.0.6
raw patch · 10 files changed
+108/−20 lines, 10 filesdep ~directorydep ~wxdep ~wxcorePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: directory, wx, wxcore
API changes (from Hackage documentation)
Files
- README.md +73/−1
- data/base/Midi.hs +6/−1
- data/example/DeBruijn.hs +8/−0
- live-sequencer.cabal +13/−14
- src/Event.hs +1/−0
- src/Option.hs +1/−1
- src/Term.hs +3/−1
- src/Time.hs +1/−0
- src/Type.hs +1/−1
- src/Utility/WX.hs +1/−1
README.md view
@@ -81,7 +81,7 @@ in order to allow offline rendering of music that you programmed within the Live-Sequencer. You may generate a standard MIDI file-using functions from the `Render` module.+using functions from the "Render" module. To this end load your song module into GHCi and call ~~~~@@ -195,6 +195,9 @@ * term size: With this limit you can prevent memory leaks.+ You may also hit the limit if you write a whole song in a big list.+ Better split the list up into sections+ and define a function for each section. * term depth: With this limit you can prevent unbalanced expression trees.@@ -283,3 +286,72 @@ * PAUSE: Toggle between real time and single step mode * FAST FORWARD: Next element in single step mode++# Tips & Tricks++## Append with overlap++The `(+:+)` operator can only handle precise concatenation of event streams.+However, in common music you also have to handle upbeats and legato.+Technically, this means that there may be events before and after a core part.+First, let us consider the case where there are events after the core part.+We define a `Block` data structure containing two or more parallel `Track`s.+For simplicity we now use only two tracks:++~~~~+type Track = [Event Message] ;+data Block = Block Track Track ;+~~~~++The first track specifies the length of the block. It cannot overlap.+If you have no other use for it, fill it with rests.+The second track can be shorter or longer than the first track.+Everything longer will be merged with subsequent `Block`s.+This is, how we convert a sequence of overlapping blocks+to a plain `Event` stream:++~~~~+consBlock :: Block -> [Event Message] -> [Event Message] ;+consBlock (Block t0 t1) y = t0 +:+ y =:= t1 ;++concatBlocks :: [Block] -> [Event Message] ;+concatBlocks = foldr consBlock [] ;+~~~~++Blocks with events before a core part+must be handled by delaying every block by the maximum time+that an event can occur before the core.++## Tempo changes++Changing the tempo within a song is a bit tricky,+especially in the presence of overlapping blocks (see above).+You want++* Consistency.+ Events that would occur at the same point in time without tempo changes+ shall occur at the same point in time in the presence of tempo changes, too.+ This rules out fixing the time periods in an event stream+ and change them later, but before merging it with other streams.++* Composability.+ You want to play only parts of the song+ or concatenate multiple parts of a song.+ This rules out external description of tempo progression.++The best I could come up with+is to extend the `Midi.Message` data type by a `SetTempo` constructor.+After you have comletely composed the song+you scan the event stream for these constructors+and change the `Wait` events accordingly.+The LiveSequencer language is untyped,+thus you could simply use a `SetTempo` constructor+as if it were a constructor of `Midi.Message`.+Though, for Haskell compatibility I suggest you wrap `Midi.Message`+in a custom datatype that adds the `SetTempo` constructor.++The MIDI file format and ALSA sequencer even support+a `SetTempo` statement natively.+However, we cannot easily make use of it,+since it is not obvious how to merge streams containing `SetTempo`+in the general case.
data/base/Midi.hs view
@@ -34,10 +34,15 @@ ) where import Function-import Pitch ( Pitch )+-- import Pitch ( Pitch )+{-+avoid dependency on Pitch+otherwise we get conflicts with custom pitch definitions in ATPS and LAC paper.+-} import Bool ( ifThenElse ) +type Pitch = Integer ; type Time = Integer ; type Velocity = Integer ; type Program = Integer ;
data/example/DeBruijn.hs view
@@ -9,6 +9,14 @@ import Prelude ( Integer, fromInteger, fromIntegral, ($), (.), (+), (-), (<), mod ) ; +{-+main :: [Event Message] ;+main =+ [Event (On 60 64), Wait 1000, Event (Off 60 64)]+ +++ [Event (On 64 64), Wait 1000, Event (Off 64 64)] ;+-}+ main :: [ Event (Channel Message) ] ; main = channel 0 $ changeTempo timeUnit $
live-sequencer.cabal view
@@ -1,15 +1,14 @@ Name: live-sequencer-Version: 0.0.5.2+Version: 0.0.6 Author: Henning Thielemann and Johannes Waldmann Maintainer: Henning Thielemann <haskell@henning-thielemann.de>, Johannes Waldmann <waldmann@imn.htwk-leipzig.de> Category: Sound, Music, GUI License: GPL License-file: LICENSE Cabal-Version: >= 1.6-Tested-With: GHC==6.12.3-Tested-With: GHC==7.2.1, GHC==7.4.2, GHC==7.6.3+Tested-With: GHC==7.8.4, GHC==8.0.1 Homepage: http://www.haskell.org/haskellwiki/Live-Sequencer-Bug-Reports: http://dfa.imn.htwk-leipzig.de/bugzilla/describecomponents.cgi?product=live-sequencer+Bug-Reports: https://hub.darcs.net/thielema/livesequencer Synopsis: Live coding of MIDI music Description: An editor shows a textual description of music (like Haskore),@@ -67,13 +66,13 @@ http/disable/HTTPServer/Option.hs Source-Repository head- Type: git- Location: git://dfa.imn.htwk-leipzig.de/srv/git/seq/+ Type: darcs+ Location: https://hub.darcs.net/thielema/livesequencer Source-Repository this- Type: git- Tag: 0.0.5.2- Location: https://github.com/thielema/livesequencer+ Tag: 0.0.6+ Type: darcs+ Location: https://hub.darcs.net/thielema/livesequencer Flag gui Description: Build the wxWidgets GUI for the sequencer@@ -169,15 +168,15 @@ containers >=0.3 && <0.6, bytestring >=0.9 && <0.11, process >=1.0 && <1.5,- directory >=1.0 && <1.3,+ directory >=1.0 && <1.4, filepath >=1.1 && <1.5, base >=4.2 && <5 Executable live-sequencer-gui If flag(gui) Build-Depends:- wx >=0.12.1 && <0.14,- wxcore >=0.12.1 && <0.14,+ wx >=0.12.1 && <0.93,+ wxcore >=0.13.2 && <0.93, stm >=2.2 && <2.5, concurrent-split >=0.0 && <0.1, transformers >=0.2.2 && <0.6,@@ -196,7 +195,7 @@ containers >=0.3 && <0.6, bytestring >=0.9 && <0.11, process >=1.0 && <1.5,- directory >=1.0 && <1.3,+ directory >=1.0 && <1.4, filepath >=1.1 && <1.5, base >=4.2 && <5 Else@@ -256,7 +255,7 @@ alsa-seq >=0.6 && <0.7, alsa-core >=0.5 && <0.6, unix >=2.4 && <2.8,- directory >=1.0 && <1.3,+ directory >=1.0 && <1.4, transformers >=0.2.2 && <0.6, base >=4.2 && <5 Else
src/Event.hs view
@@ -362,6 +362,7 @@ t <- fmap (mappend (ALSA.latencyNano sq)) $ AccM.get stateTime ALSA.sendAllNotesOffLater t ALSA.forwardStoppedQueue t+-- AccM.set Event.stateTime t prepare ::
src/Option.hs view
@@ -39,7 +39,7 @@ -- the formatted value might look ugly defltLatencyStr :: String-defltLatencyStr = "0.05"+defltLatencyStr = "0.2" getDeflt :: IO Option getDeflt = do
src/Term.hs view
@@ -19,6 +19,7 @@ import Control.Monad ( liftM2, mzero ) import Control.Functor.HT ( void ) import Data.Char (isUpper, isLower)+-- import Data.Eq.HT (equating) import Data.Ord (comparing) @@ -29,9 +30,10 @@ Identifier { range :: Range, name :: String } instance Eq Identifier where--- | FIXME: this is ignoring the module.+-- FIXME: this is ignoring the module. -- for a complete implementation, we'd need fully qualified names i == j = name i == name j+-- (==) = equating name instance Ord Identifier where compare = comparing name
src/Time.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE EmptyDataDecls #-}+{- | Similar to "Data.Fixed" -} module Time where import Control.Concurrent ( threadDelay )
src/Type.hs view
@@ -34,7 +34,7 @@ } -operators :: [[([Char], Assoc)]]+operators :: [[(String, Assoc)]] operators = [ [ ( "->", AssocRight ) ] -- , [ ( ",", AssocRight) ]
src/Utility/WX.hs view
@@ -37,4 +37,4 @@ (\nb -> void . WXCMZ.notebookSetSelection nb) splitterWindowSetSashGravity :: WX.SplitterWindow a -> Double -> IO ()-splitterWindowSetSashGravity _w _g = return ()+splitterWindowSetSashGravity = WXCMZ.splitterWindowSetSashGravity