music-parts 1.7.1 → 1.7.2
raw patch · 8 files changed
+697/−194 lines, 8 filesdep +music-pitchdep −randomdep −timedep ~lensPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: music-pitch
Dependencies removed: random, time
Dependency ranges changed: lens
API changes (from Hackage documentation)
- Music.Parts: OtherInstrument :: String -> Instrument
- Music.Parts: Solo :: Solo
- Music.Parts: StdInstrument :: Int -> Instrument
- Music.Parts: Tutti :: Solo
- Music.Parts: containsSubpart :: Subpart -> Subpart -> Bool
- Music.Parts: data BasicPart
- Music.Parts: data Division
- Music.Parts: data Instrument
- Music.Parts: data Solo
- Music.Parts: data Subpart
- Music.Parts: divisions :: Int -> [Division]
- Music.Parts: getDivision :: Division -> (Int, Int)
- Music.Parts: instance Default BasicPart
- Music.Parts: instance Default Division
- Music.Parts: instance Default Instrument
- Music.Parts: instance Default Solo
- Music.Parts: instance Default Subpart
- Music.Parts: instance Enum BasicPart
- Music.Parts: instance Enum Instrument
- Music.Parts: instance Enum Solo
- Music.Parts: instance Enum a => Enum (Option a)
- Music.Parts: instance Eq BasicPart
- Music.Parts: instance Eq Division
- Music.Parts: instance Eq Instrument
- Music.Parts: instance Eq Solo
- Music.Parts: instance Eq Subpart
- Music.Parts: instance Integral BasicPart
- Music.Parts: instance Integral a => Integral (First a)
- Music.Parts: instance Integral a => Integral (Option a)
- Music.Parts: instance Monoid BasicPart
- Music.Parts: instance Monoid Subpart
- Music.Parts: instance Num BasicPart
- Music.Parts: instance Num a => Num (First a)
- Music.Parts: instance Num a => Num (Option a)
- Music.Parts: instance Ord BasicPart
- Music.Parts: instance Ord Division
- Music.Parts: instance Ord Instrument
- Music.Parts: instance Ord Solo
- Music.Parts: instance Ord Subpart
- Music.Parts: instance Real BasicPart
- Music.Parts: instance Real a => Real (First a)
- Music.Parts: instance Real a => Real (Option a)
- Music.Parts: instance Semigroup BasicPart
- Music.Parts: instance Semigroup Subpart
- Music.Parts: instance Show BasicPart
- Music.Parts: instance Show Division
- Music.Parts: instance Show Instrument
- Music.Parts: instance Show Solo
- Music.Parts: instance Show Subpart
- Music.Parts: instance Typeable BasicPart
+ Data.Semigroup.Option.Instances: instance Enum a => Enum (Option a)
+ Data.Semigroup.Option.Instances: instance Integral a => Integral (First a)
+ Data.Semigroup.Option.Instances: instance Integral a => Integral (Option a)
+ Data.Semigroup.Option.Instances: instance Num a => Num (First a)
+ Data.Semigroup.Option.Instances: instance Num a => Num (Option a)
+ Data.Semigroup.Option.Instances: instance Real a => Real (First a)
+ Data.Semigroup.Option.Instances: instance Real a => Real (Option a)
+ Music.Parts.Basic: data BasicPart
+ Music.Parts.Basic: instance Default BasicPart
+ Music.Parts.Basic: instance Enum BasicPart
+ Music.Parts.Basic: instance Eq BasicPart
+ Music.Parts.Basic: instance Integral BasicPart
+ Music.Parts.Basic: instance Monoid BasicPart
+ Music.Parts.Basic: instance Num BasicPart
+ Music.Parts.Basic: instance Ord BasicPart
+ Music.Parts.Basic: instance Real BasicPart
+ Music.Parts.Basic: instance Semigroup BasicPart
+ Music.Parts.Basic: instance Show BasicPart
+ Music.Parts.Basic: instance Typeable BasicPart
+ Music.Parts.Division: data Division
+ Music.Parts.Division: divisions :: Int -> [Division]
+ Music.Parts.Division: getDivision :: Division -> (Int, Int)
+ Music.Parts.Division: instance Default Division
+ Music.Parts.Division: instance Eq Division
+ Music.Parts.Division: instance Ord Division
+ Music.Parts.Division: instance Show Division
+ Music.Parts.Division: showDivision :: Division -> String
+ Music.Parts.Division: showDivisionR :: Division -> String
+ Music.Parts.Instrument: OtherInstrument :: String -> Instrument
+ Music.Parts.Instrument: StdInstrument :: Int -> Instrument
+ Music.Parts.Instrument: data Instrument
+ Music.Parts.Instrument: instance Default Instrument
+ Music.Parts.Instrument: instance Enum Instrument
+ Music.Parts.Instrument: instance Eq Instrument
+ Music.Parts.Instrument: instance Ord Instrument
+ Music.Parts.Instrument: instance Show Instrument
+ Music.Parts.Solo: Solo :: Solo
+ Music.Parts.Solo: Tutti :: Solo
+ Music.Parts.Solo: data Solo
+ Music.Parts.Solo: instance Default Solo
+ Music.Parts.Solo: instance Enum Solo
+ Music.Parts.Solo: instance Eq Solo
+ Music.Parts.Solo: instance Ord Solo
+ Music.Parts.Solo: instance Show Solo
+ Music.Parts.Subpart: Subpart :: [Division] -> Subpart
+ Music.Parts.Subpart: containsSubpart :: Subpart -> Subpart -> Bool
+ Music.Parts.Subpart: instance Default Subpart
+ Music.Parts.Subpart: instance Eq Subpart
+ Music.Parts.Subpart: instance Monoid Subpart
+ Music.Parts.Subpart: instance Ord Subpart
+ Music.Parts.Subpart: instance Semigroup Subpart
+ Music.Parts.Subpart: instance Show Subpart
+ Music.Parts.Subpart: newtype Subpart
Files
- music-parts.cabal +38/−7
- src/Data/Semigroup/Option/Instances.hs +49/−0
- src/Music/Parts.hs +102/−187
- src/Music/Parts/Basic.hs +44/−0
- src/Music/Parts/Division.hs +75/−0
- src/Music/Parts/Instrument.hs +293/−0
- src/Music/Parts/Solo.hs +41/−0
- src/Music/Parts/Subpart.hs +55/−0
music-parts.cabal view
@@ -1,11 +1,11 @@ name: music-parts-version: 1.7.1+version: 1.7.2 author: Hans Hoglund maintainer: Hans Hoglund license: BSD3 license-file: COPYING-synopsis: To be written.+synopsis: Musical instruments, parts and playing techniques. category: Music tested-with: GHC build-type: Simple@@ -23,20 +23,51 @@ library build-depends: base >= 4 && < 5, aeson >= 0.7.0.6 && < 1,- lens >= 4.3 && < 4.4,+ lens >= 4.3.3 && < 4.4, semigroups >= 0.13.0.1 && < 1,- time,- random, semigroupoids, data-default, adjunctions >= 4.1 && < 5,- roman-numerals- exposed-modules: Music.Parts+ roman-numerals,+ music-pitch == 1.7.2+ exposed-modules: Data.Semigroup.Option.Instances+ + Music.Parts+ + Music.Parts.Basic+ + -- -- TODO choral voicings etc+ -- -- TODO use single type or many? How to do "subtyping"+ -- -- Prisms, TFs, something else?+ -- + -- -- TODO require pitch dep+ -- Music.Parts.Range++ Music.Parts.Division+ Music.Parts.Subpart+ Music.Parts.Solo+ + -- TODO "ensembles", essentially echoing the instrument hierarchy but in plural, i.e. (flute :: Instrument, flutes :: Part)+ -- TODO write something nice about how division works, why we don't usually export flute1 etc. -- Music.Parts.Strings -- Music.Parts.Woodwind -- Music.Parts.Brass -- Music.Parts.Percussion -- Music.Parts.Rhythm -- Music.Parts.Other+ + Music.Parts.Instrument+ -- Music.Parts.Instrument.Strings+ -- Music.Parts.Instrument.Woodwind+ -- Music.Parts.Instrument.Brass+ -- Music.Parts.Instrument.Percussion+ -- Music.Parts.Instrument.Rhythm+ -- Music.Parts.Instrument.Other+ + -- TODO a way to classify parts+ -- so we know what goes on which staff, what kind of bracket to generate etc+ + -- TODO some sub-subdivision (not for player allocation, but hand/finger)+ hs-source-dirs: src default-language: Haskell2010
+ src/Data/Semigroup/Option/Instances.hs view
@@ -0,0 +1,49 @@++module Data.Semigroup.Option.Instances where++import Control.Applicative+import Control.Lens (toListOf)+import Data.Default+import Data.Functor.Adjunction (unzipR)+import qualified Data.List+import Data.Maybe+import Data.Semigroup+import Data.Traversable (traverse)+import Data.Typeable+import Text.Numeral.Roman (toRoman)++-- TODO move++instance Num a => Num (Option a) where+ (+) = liftA2 (+)+ (-) = liftA2 (-)+ (*) = liftA2 (*)+ abs = fmap abs+ signum = fmap signum+ fromInteger = pure . fromInteger+instance Integral a => Integral (Option a) where+ quotRem x y = unzipR $ liftA2 quotRem x y+ toInteger = toInteger . get where get = (head.toListOf traverse)+instance Real a => Real (Option a) where+ toRational = toRational . get where get = (head.toListOf traverse)+instance Enum a => Enum (Option a) where+ fromEnum = fromEnum . get where get = (head.toListOf traverse)+ toEnum = pure . toEnum++-- TODO move++instance Num a => Num (First a) where+ (+) = liftA2 (+)+ (-) = liftA2 (-)+ (*) = liftA2 (*)+ abs = fmap abs+ signum = fmap signum+ fromInteger = pure . fromInteger+instance Integral a => Integral (First a) where+ quotRem x y = unzipR $ liftA2 quotRem x y+ toInteger = toInteger . get where get = (head.toListOf traverse)+instance Real a => Real (First a) where+ toRational = toRational . get where get = (head.toListOf traverse)+-- instance Enum a => Enum (First a) where+ -- toEnum = toEnum . get where get = (head.toListOf traverse)+ -- fromEnum = pure . fromEnum
src/Music/Parts.hs view
@@ -1,12 +1,11 @@ -{-# LANGUAGE- GeneralizedNewtypeDeriving,- DeriveDataTypeable,- TypeFamilies #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-} ------------------------------------------------------------------------------------ -- |--- Copyright : (c) Hans Hoglund 2012+-- Copyright : (c) Hans Hoglund 2012-2015 -- -- License : BSD-style --@@ -14,7 +13,7 @@ -- Stability : experimental -- Portability : non-portable (TF,GNTD) ----- Part representation.+-- Provides various representaitons of musical instruments, subdivisions and parts. -- ------------------------------------------------------------------------------------- @@ -22,36 +21,34 @@ -- * Terminology -- $terminology - + -- * Subparts- Division,- divisions,- getDivision,- Subpart,+ module Music.Parts.Division,+ module Music.Parts.Subpart, + -- * Solo vs. tutti+ module Music.Parts.Solo,+ -- * Instruments- Instrument(..),- -- TODO hide impl+ module Music.Parts.Instrument, -- * Parts- Solo(..), Part(..), divide, containsPart,- containsSubpart, solo, tutti,- + -- ** Instruments etc piccoloFlute, flute, altoFlute, bassFlute,- + oboe, corAnglais, heckelphone,- + ebClarinet, clarinet, aClarinet,@@ -61,10 +58,10 @@ altoSax, tenorSax, baritoneSax,- + bassoon, contraBassoon,- + horn, piccoloTrumpet, trumpet,@@ -74,10 +71,10 @@ trombone, bassTrombone, tuba,- + timpani, piano,- + celesta, glockenspiel, vibraphone,@@ -86,10 +83,10 @@ xylorimba, tubularBells, dulcimer,- + accordion, harmonica,- + violin, viola, cello,@@ -116,7 +113,7 @@ trombones, trumpets1, trumpets2,- trombones1, + trombones1, trombones2, tubas, @@ -137,22 +134,28 @@ -- * Basic- -- TODO move- BasicPart,+ module Music.Parts.Basic ) where -import Data.Default-import Data.Semigroup-import Data.Typeable-import Data.Maybe-import Data.Traversable (traverse)-import Control.Lens (toListOf)-import Data.Functor.Adjunction (unzipR)-import Control.Applicative-import Text.Numeral.Roman (toRoman)+import Control.Applicative+import Control.Lens (toListOf)+import Data.Default+import Data.Functor.Adjunction (unzipR) import qualified Data.List+import Data.Maybe+import Data.Semigroup+import Data.Semigroup.Option.Instances+import Data.Traversable (traverse)+import Data.Typeable+import Text.Numeral.Roman (toRoman) +import Music.Parts.Basic+import Music.Parts.Subpart+import Music.Parts.Division+import Music.Parts.Solo+import Music.Parts.Instrument+ {- $terminology Parts represent a subset of a group of performers. It is mainly used for instrumental and@@ -170,92 +173,11 @@ separately, so i.e. /Violin solo II/ (as in a double concerto) is distinct from /Violin II/. -}- -{-- For each part we want to know:- - Classification:- - Type: (i.e. woodwind)- - Family: (i.e. saxophone)- - Range: (i.e. tenor) - - Range (i.e. [c_:e'])- - Transposition:- sounding = written .+^ transp - - Suggested clefs--} --- |--- A division represents a subset of a finite group of performers.------ For example a group may be divided into three equal divisions,--- designated @(0, 3)@, @(1, 3)@ and @(2, 3)@ respectively.----newtype Division = Division { getDivision :: (Int, Int) }- deriving (Eq, Ord, Show)-instance Default Division where- def = Division (0,1) -showDivisionR :: Division -> String-showDivisionR = toRoman . succ . fst . getDivision -showDivision :: Division -> String-showDivision = show . succ . fst . getDivision---- | Get all possible divisions for a given divisor in ascending order.-divisions :: Int -> [Division]-divisions n = [Division (x,n) | x <- [0..n-1]]---- | Divide a part into @n@ subparts.-divide :: Int -> Part -> [Part]-divide n (Part solo instr subp) = fmap (\x -> Part solo instr (subp <> Subpart [x])) $ divisions n---- |--- A subpart is a potentially infinite sequence of divisions, each typically--- designated using a new index type, i.e. @I.1.2@.------ The empty subpart (also known as 'def') represents all the players of the group,--- or in the context of 'Part', all players of the given instrument.--- -newtype Subpart = Subpart [Division]- deriving (Eq, Ord, Default, Semigroup, Monoid)--instance Show Subpart where- show (Subpart ps) = Data.List.intercalate "." $ mapFR showDivisionR showDivision $ ps- where- mapFR f g [] = []- mapFR f g (x:xs) = f x : fmap g xs---- | An 'Instrument' represents the set of all instruments of a given type.-data Instrument - = StdInstrument Int- | OtherInstrument String- deriving (Eq)--instance Show Instrument where - show (StdInstrument x) = fromMaybe "(unknown)" $ gmInstrName x- show (OtherInstrument str) = str-instance Enum Instrument where- toEnum = StdInstrument- fromEnum (StdInstrument x) = x- fromEnum (OtherInstrument _) = error "Instrument.fromEnum used on unknown instrument"--instance Ord Instrument where- StdInstrument x `compare` StdInstrument y = gmScoreOrder x `compare` gmScoreOrder y---- | This instance is quite arbitrary but very handy.-instance Default Instrument where- def = StdInstrument 0--data Solo- = Solo- | Tutti- deriving (Eq, Show, Ord, Enum)--instance Default Solo where- def = Tutti -- -- | A part is a subdivided group of instruments of a given type. -- data Part = Part Solo Instrument Subpart@@ -285,17 +207,18 @@ instance Default Part where def = Part def def def --- | --- @a \`containsPart\` b@ holds if the set of players represented by a is an improper subset of the +-- |+-- @a \`containsPart\` b@ holds if the set of players represented by a is an improper subset of the -- set of players represented by b. containsPart :: Part -> Part -> Bool-Part solo1 instr1 subp1 `containsPart` Part solo2 instr2 subp2 = - solo1 == solo2 +Part solo1 instr1 subp1 `containsPart` Part solo2 instr2 subp2 =+ solo1 == solo2 && instr1 == instr2 && subp1 `containsSubpart` subp2 -containsSubpart :: Subpart -> Subpart -> Bool-Subpart x `containsSubpart` Subpart y = y `Data.List.isPrefixOf` x+-- | Divide a part into @n@ subparts.+divide :: Int -> Part -> [Part]+divide n (Part solo instr subp) = fmap (\x -> Part solo instr (subp <> Subpart [x])) $ divisions n solo instr = Part Solo instr def tutti instr = Part Tutti instr def@@ -398,8 +321,8 @@ {- Score order: Woodwinds: 1- Brass: 2 - Timpani: 3 + Brass: 2+ Timpani: 3 Percussion: 4 Keyboard/Harp 5 Singers 6@@ -408,7 +331,8 @@ -} gmInstrs :: [(Int, (Int, Double, Int, String))] gmInstrs = [- (0, (0, 5.0, 0, "Acoustic Grand Piano")),+ -- (0, (0, 5.0, 0, "Acoustic Grand Piano")),+ (0, (0, 5.0, 0, "Piano")), (1, (0, 5.0, 0, "Bright Acoustic Piano")), (2, (0, 5.0, 0, "Electric Grand Piano")), (3, (0, 5.0, 0, "Honky-tonk Piano")),@@ -612,7 +536,7 @@ --- +-- -- data Section -- = Woodwind -- | Brass@@ -620,16 +544,16 @@ -- | Keyboard -- | Voices -- | Strings--- +-- -- data VoicePart -- = Soprano -- | MezzoSoprano--- | Alto+-- | Alto -- | Tenor -- | Baritone -- | Bass--- --- +--+-- -- data GMInstrumentType -- = GMPiano -- | GMChromaticPercussion@@ -644,22 +568,22 @@ -- | GMSynthLead -- | GMSynthPad -- | GMSynthEffects--- | GMEthnic+-- | GMEthnic -- | GMPercussive--- | GMSoundEffects +-- | GMSoundEffects - + {- ## Terminology: Voice vs Part- + A voice is a container of notes (non-overlapping)- + A part is an identifier for a set of singers/musicians AND all the notes in a score designated for this set of performers. Part extraction has the type- + extractParts :: HasPart a => Score a -> [Score a]- + I.e. in a score for piano and ensemble, certain notes may be *in the piano part*, i.e. designated for the piano. Typically, a part is monophonic or polyphonic. A monophonic part is a voice, i.e.@@ -669,10 +593,10 @@ -- | Fails for polyphonic scores. scoreToVoice :: Score a -> Voice (Maybe a)- + A polyphonic score contains several voices, i.e. - scoreToVoices :: Score a -> [Voice (Maybe a)] + scoreToVoices :: Score a -> [Voice (Maybe a)] A part is any type a that satisfies (Ord a, Show a).@@ -681,8 +605,8 @@ class HasPartName a where partName :: a -> String partAbbr :: a -> String- - ++ These contraints are used when printing scores (to get the order of the parts and their name). Vln1, Vln2 etc.@@ -709,57 +633,48 @@ partGroup :: (Part -> [Group] -> a) -> Group -> a tree :: (a -> [Tree a] -> b) -> Tree a -> b- - ++ data MyPart = Fl- | Sop+ | Sop | Vl1- | Vl2+ | Vl2 | Pno -} -- TODO move-instance Num a => Num (Option a) where- (+) = liftA2 (+)- (-) = liftA2 (-)- (*) = liftA2 (*)- abs = fmap abs- signum = fmap signum- fromInteger = pure . fromInteger-instance Integral a => Integral (Option a) where- quotRem x y = unzipR $ liftA2 quotRem x y- toInteger = toInteger . get where get = (head.toListOf traverse)-instance Real a => Real (Option a) where- toRational = toRational . get where get = (head.toListOf traverse)-instance Enum a => Enum (Option a) where- fromEnum = fromEnum . get where get = (head.toListOf traverse)- toEnum = pure . toEnum--instance Num a => Num (First a) where- (+) = liftA2 (+)- (-) = liftA2 (-)- (*) = liftA2 (*)- abs = fmap abs- signum = fmap signum- fromInteger = pure . fromInteger-instance Integral a => Integral (First a) where- quotRem x y = unzipR $ liftA2 quotRem x y- toInteger = toInteger . get where get = (head.toListOf traverse)-instance Real a => Real (First a) where- toRational = toRational . get where get = (head.toListOf traverse)--- instance Enum a => Enum (First a) where- -- toEnum = toEnum . get where get = (head.toListOf traverse)- -- fromEnum = pure . fromEnum--newtype BasicPart = BasicPart { getBasicPart :: Option (First Integer) }- deriving (Eq, Ord, Num, Integral, Real, Enum, Typeable, Semigroup, Monoid)--instance Default BasicPart where- def = mempty--instance Show BasicPart where- show _ = ""+-- instance Num a => Num (Option a) where+-- (+) = liftA2 (+)+-- (-) = liftA2 (-)+-- (*) = liftA2 (*)+-- abs = fmap abs+-- signum = fmap signum+-- fromInteger = pure . fromInteger+-- instance Integral a => Integral (Option a) where+-- quotRem x y = unzipR $ liftA2 quotRem x y+-- toInteger = toInteger . get where get = (head.toListOf traverse)+-- instance Real a => Real (Option a) where+-- toRational = toRational . get where get = (head.toListOf traverse)+-- instance Enum a => Enum (Option a) where+-- fromEnum = fromEnum . get where get = (head.toListOf traverse)+-- toEnum = pure . toEnum+--+-- instance Num a => Num (First a) where+-- (+) = liftA2 (+)+-- (-) = liftA2 (-)+-- (*) = liftA2 (*)+-- abs = fmap abs+-- signum = fmap signum+-- fromInteger = pure . fromInteger+-- instance Integral a => Integral (First a) where+-- quotRem x y = unzipR $ liftA2 quotRem x y+-- toInteger = toInteger . get where get = (head.toListOf traverse)+-- instance Real a => Real (First a) where+-- toRational = toRational . get where get = (head.toListOf traverse)+-- -- instance Enum a => Enum (First a) where+-- -- toEnum = toEnum . get where get = (head.toListOf traverse)+-- -- fromEnum = pure . fromEnum piccoloFlutes = tutti piccoloFlute@@ -1680,4 +1595,4 @@ <sound id="wood.tonetang"/> <sound id="wood.wood-block"/> -}- +
+ src/Music/Parts/Basic.hs view
@@ -0,0 +1,44 @@++{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-}++------------------------------------------------------------------------------------+-- |+-- Copyright : (c) Hans Hoglund 2012-2015+--+-- License : BSD-style+--+-- Maintainer : hans@hanshoglund.se+-- Stability : experimental+-- Portability : non-portable (TF,GNTD)+--+-- Provides various representaitons of musical instruments, subdivisions and parts.+--+-------------------------------------------------------------------------------------++module Music.Parts.Basic (+ BasicPart+ ) where++import Control.Applicative+import Control.Lens (toListOf)+import Data.Default+import Data.Functor.Adjunction (unzipR)+import qualified Data.List+import Data.Maybe+import Data.Semigroup+import Data.Traversable (traverse)+import Data.Typeable+import Text.Numeral.Roman (toRoman)+import Data.Semigroup.Option.Instances++newtype BasicPart = BasicPart { getBasicPart :: Option (First Integer) }+ deriving (Eq, Ord, Num, Integral, Real, Enum, Typeable, Semigroup, Monoid)++instance Default BasicPart where+ def = mempty++instance Show BasicPart where+ show _ = ""+
+ src/Music/Parts/Division.hs view
@@ -0,0 +1,75 @@++{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-}++------------------------------------------------------------------------------------+-- |+-- Copyright : (c) Hans Hoglund 2012-2015+--+-- License : BSD-style+--+-- Maintainer : hans@hanshoglund.se+-- Stability : experimental+-- Portability : non-portable (TF,GNTD)+--+-------------------------------------------------------------------------------------++module Music.Parts.Division (+ Division,+ divisions,+ getDivision,+ showDivision,+ showDivisionR,+ ) where++import Control.Applicative+import Control.Lens (toListOf)+import Data.Default+import Data.Functor.Adjunction (unzipR)+import qualified Data.List+import Data.Maybe+import Data.Semigroup+import Data.Semigroup.Option.Instances+import Data.Traversable (traverse)+import Data.Typeable+import Text.Numeral.Roman (toRoman)++++{-+ For each part we want to know:+ - Classification:+ - Type: (i.e. woodwind)+ - Family: (i.e. saxophone)+ - Range: (i.e. tenor)+ - Range (i.e. [c_:e'])+ - Transposition:+ sounding = written .+^ transp+ - Suggested clefs+-}++-- |+-- A division represents a subset of a finite group of performers.+--+-- For example a group may be divided into three equal divisions,+-- designated @(0, 3)@, @(1, 3)@ and @(2, 3)@ respectively.+--+newtype Division = Division { getDivision :: (Int, Int) }+ deriving (Eq, Ord, Show)++instance Default Division where+ def = Division (0,1)++-- | Show division in roman numerals.+showDivisionR :: Division -> String+showDivisionR = toRoman . succ . fst . getDivision++-- | Show division in ordinary numerals.+showDivision :: Division -> String+showDivision = show . succ . fst . getDivision++-- | Get all possible divisions for a given divisor in ascending order.+divisions :: Int -> [Division]+divisions n = [Division (x,n) | x <- [0..n-1]]+
+ src/Music/Parts/Instrument.hs view
@@ -0,0 +1,293 @@++{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-}++------------------------------------------------------------------------------------+-- |+-- Copyright : (c) Hans Hoglund 2012-2015+--+-- License : BSD-style+--+-- Maintainer : hans@hanshoglund.se+-- Stability : experimental+-- Portability : non-portable (TF,GNTD)+--+-------------------------------------------------------------------------------------++module Music.Parts.Instrument +(+Instrument(..),+-- TODO hide impl+)+where++import Control.Applicative+import Control.Lens (toListOf)+import Data.Default+import Data.Functor.Adjunction (unzipR)+import qualified Data.List+import Data.Maybe+import Data.Semigroup+import Data.Semigroup.Option.Instances+import Data.Traversable (traverse)+import Data.Typeable+import Text.Numeral.Roman (toRoman)++-- | An 'Instrument' represents the set of all instruments of a given type.+data Instrument+ = StdInstrument Int+ | OtherInstrument String+ deriving (Eq)++instance Show Instrument where+ show (StdInstrument x) = fromMaybe "(unknown)" $ gmInstrName x+ show (OtherInstrument str) = str+instance Enum Instrument where+ toEnum = StdInstrument+ fromEnum (StdInstrument x) = x+ fromEnum (OtherInstrument _) = error "Instrument.fromEnum used on unknown instrument"++instance Ord Instrument where+ StdInstrument x `compare` StdInstrument y = gmScoreOrder x `compare` gmScoreOrder y++-- | This instance is quite arbitrary but very handy.+instance Default Instrument where+ def = StdInstrument 0+++-- TODO consolidate++gmClef :: Int -> Int+gmMidiChannel :: Int -> Int+gmScoreOrder :: Int -> Double++gmMidiChannel = fromMaybe 0 . fmap get . (`lookup` gmInstrs)+ where get (x,_,_,_) = x++gmScoreOrder = fromMaybe 0 . fmap get . (`lookup` gmInstrs)+ where get (_,x,_,_) = x++gmClef = fromMaybe 0 . fmap get . (`lookup` gmInstrs)+ where get (_,_,x,_) = x+++gmInstrName :: Int -> Maybe String+gmInstrName = fmap get . (`lookup` gmInstrs)+ where get (_,_,_,x) = x++-- (midi program, (def midi ch, score order, def clef 0=g/1=c/2=f, name))+{-+ Score order:+ Woodwinds: 1+ Brass: 2+ Timpani: 3+ Percussion: 4+ Keyboard/Harp 5+ Singers 6+ Strings: 7+ Bass: 8+-}+gmInstrs :: [(Int, (Int, Double, Int, String))]+gmInstrs = [+ -- (0, (0, 5.0, 0, "Acoustic Grand Piano")),+ (0, (0, 5.0, 0, "Piano")),+ (1, (0, 5.0, 0, "Bright Acoustic Piano")),+ (2, (0, 5.0, 0, "Electric Grand Piano")),+ (3, (0, 5.0, 0, "Honky-tonk Piano")),+ (4, (0, 5.0, 0, "Electric Piano 1")),+ (5, (0, 5.0, 0, "Electric Piano 2")),+ (6, (0, 5.0, 0, "Harpsichord")),+ (7, (0, 5.0, 0, "Clavinet")),++ (8, (0, 5.0, 0, "Celesta")),+ (9, (0, 5.0, 0, "Glockenspiel")),+ (9, (0, 5.0, 0, "Music Box")),+ (11, (0, 4.0, 0, "Vibraphone")),+ (12, (0, 4.0, 0, "Marimba")),+ (13, (0, 4.0, 0, "Xylophone")),+ (14, (0, 4.0, 0, "Tubular Bells")),+ (15, (0, 4.0, 0, "Dulcimer")),++ (16, (0, 5.0, 0, "Drawbar Organ")),+ (17, (0, 5.0, 0, "Percussive Organ")),+ (18, (0, 5.0, 0, "Rock Organ")),+ (19, (0, 5.0, 0, "Church Organ")),+ (20, (0, 5.0, 0, "Reed Organ")),+ (21, (0, 5.0, 0, "Accordion")),+ (22, (0, 5.0, 0, "Harmonica")),+ (23, (0, 5.0, 0, "Tango Accordion")),++ (24, (0, 5.0, 0, "Acoustic Guitar (nylon)")),+ (25, (0, 5.0, 0, "Acoustic Guitar (steel)")),+ (26, (0, 5.0, 0, "Electric Guitar (jazz)")),+ (27, (0, 5.0, 0, "Electric Guitar (clean)")),+ (28, (0, 5.0, 0, "Electric Guitar (muted)")),+ (29, (0, 5.0, 0, "Overdriven Guitar")),+ (30, (0, 5.0, 0, "Distortion Guitar")),+ (31, (0, 5.0, 0, "Guitar Harmonics")),++ (32, (0, 8.0, 2, "Acoustic Bass")),+ (33, (0, 8.0, 2, "Electric Bass (finger)")),+ (34, (0, 8.0, 2, "Electric Bass (pick)")),+ (35, (0, 8.0, 2, "Fretless Bass")),+ (36, (0, 8.0, 2, "Slap Bass 1")),+ (37, (0, 8.0, 2, "Slap Bass 2")),+ (38, (0, 8.0, 2, "Synth Bass 1")),+ (39, (0, 8.0, 2, "Synth Bass 2")),++ (40, (12, 7.1, 0, "Violin")),+ (41, (13, 7.2, 1, "Viola")),+ -- (42, (0, 1.0, 0, "Cello")),+ (42, (14, 7.3, 2, "Violoncello")),+ (43, (15, 7.4, 2, "Contrabass")),+ (44, (0, 7.0, 0, "Tremolo Strings")),+ (45, (0, 7.0, 0, "Pizzicato Strings")),+ (46, (11, 5.9, 0, "Orchestral Harp")),++ (47, (8, 2.5, 2, "Timpani")),++ (48, (0, 7.0, 0, "String Ensemble 1")),+ (49, (0, 7.0, 0, "String Ensemble 2")),+ (50, (0, 7.0, 0, "Synth Strings 1")),+ (51, (0, 7.0, 0, "Synth Strings 2")),++ (52, (0, 1.0, 0, "Choir Aahs")),+ (53, (0, 1.0, 0, "Voice Oohs")),+ (54, (0, 1.0, 0, "Synth Choir")),+ (55, (0, 1.0, 0, "Orchestra Hit")),++ (56, (5, 2.2, 0, "Trumpet in Bb")),+ (57, (6, 2.3, 2, "Trombone")),+ (58, (7, 2.4, 2, "Tuba")),+ (59, (0, 2.2, 0, "Muted Trumpet")),+ -- (60, (4, 2.1, 0, "French Horn")),+ (60, (4, 2.1, 0, "Horn in F")),+ (61, (0, 2.0, 0, "Brass Section")),+ (62, (0, 2.0, 0, "Synth Brass 1")),+ (63, (0, 2.0, 0, "Synth Brass 2")),++ (64, (0, 1.51, 0, "Soprano Sax")),+ (65, (0, 1.52, 0, "Alto Sax")),+ (66, (0, 1.53, 0, "Tenor Sax")),+ (67, (0, 1.54, 0, "Baritone Sax")),+ (68, (1, 1.3, 0, "Oboe")),+ (69, (1, 1.4, 0, "English Horn")),+ (70, (3, 1.7, 2, "Bassoon")),+ (71, (2, 1.6, 0, "Clarinet in Bb")),++ (72, (0, 1.1, 0, "Piccolo")),+ (73, (0, 1.2, 0, "Flute")),+ (74, (0, 1.0, 0, "Recorder")),+ (75, (0, 1.0, 0, "Pan Flute")),+ (76, (0, 1.0, 0, "Blown bottle")),+ (77, (0, 1.0, 0, "Shakuhachi")),+ (78, (0, 1.0, 0, "Whistle")),+ (79, (0, 1.0, 0, "Ocarina")),++ (80, (0, 1.0, 0, "Lead 1 (square)")),+ (81, (0, 1.0, 0, "Lead 2 (sawtooth)")),+ (82, (0, 1.0, 0, "Lead 3 (calliope)")),+ (83, (0, 1.0, 0, "Lead 4 (chiff)")),+ (84, (0, 1.0, 0, "Lead 5 (charang)")),+ (85, (0, 1.0, 0, "Lead 6 (voice)")),+ (86, (0, 1.0, 0, "Lead 7 (fifths)")),+ (87, (0, 1.0, 0, "Lead 8 (bass + lead)")),++ (88, (0, 1.0, 0, "Pad 1 (new age)")),+ (89, (0, 1.0, 0, "Pad 2 (warm)")),+ (90, (0, 1.0, 0, "Pad 3 (polysynth)")),+ (91, (0, 1.0, 0, "Pad 4 (choir)")),+ (92, (0, 1.0, 0, "Pad 5 (bowed)")),+ (93, (0, 1.0, 0, "Pad 6 (metallic)")),+ (94, (0, 1.0, 0, "Pad 7 (halo)")),+ (95, (0, 1.0, 0, "Pad 8 (sweep)")),++ (96, (0, 1.0, 0, "FX 1 (rain)")),+ (97, (0, 1.0, 0, "FX 2 (soundtrack)")),+ (98, (0, 1.0, 0, "FX 3 (crystal)")),+ (99, (0, 1.0, 0, "FX 4 (atmosphere)")),+ (100, (0, 1.0, 0, "FX 5 (brightness)")),+ (101, (0, 1.0, 0, "FX 6 (goblins)")),+ (102, (0, 1.0, 0, "FX 7 (echoes)")),+ (103, (0, 1.0, 0, "FX 8 (sci-fi)")),++ (104, (0, 1.0, 0, "Sitar")),+ (105, (0, 1.0, 0, "Banjo")),+ (106, (0, 1.0, 0, "Shamisen")),+ (107, (0, 1.0, 0, "Koto")),+ (108, (0, 1.0, 0, "Kalimba")),+ (109, (0, 1.0, 0, "Bagpipe")),+ (110, (0, 1.0, 0, "Fiddle")),+ (111, (0, 1.0, 0, "Shanai")),++ (112, (0, 1.0, 0, "Tinkle Bell")),+ (113, (0, 1.0, 0, "Agogo")),+ (114, (0, 1.0, 0, "Steel Drums")),+ (115, (0, 1.0, 0, "Woodblock")),+ (116, (0, 1.0, 0, "Taiko Drum")),+ (117, (0, 1.0, 0, "Melodic Tom")),+ (118, (0, 1.0, 0, "Synth Drum")),+ (119, (0, 1.0, 0, "Reverse Cymbal")),++ (120, (0, 1.0, 0, "Guitar Fret Noise")),+ (121, (0, 1.0, 0, "Breath Noise")),+ (122, (0, 1.0, 0, "Seashore")),+ (123, (0, 1.0, 0, "Bird Tweet")),+ (124, (0, 1.0, 0, "Telephone Ring")),+ (125, (0, 1.0, 0, "Helicopter")),+ (126, (0, 1.0, 0, "Applause")),+ (127, (0, 1.0, 0, "Gunshot"))+ ]++gmPerc :: [(Int, String)]+gmPerc = [+ (35, "Bass Drum 2"),+ (36, "Bass Drum 1"),+ (37, "Side Stick/Rimshot"),+ (38, "Snare Drum 1"),+ (39, "Hand Clap"),+ (40, "Snare Drum 2"),+ (41, "Low Tom 2"),+ (42, "Closed Hi-hat"),+ (43, "Low Tom 1"),+ (44, "Pedal Hi-hat"),+ (45, "Mid Tom 2"),+ (46, "Open Hi-hat"),+ (47, "Mid Tom 1"),+ (48, "High Tom 2"),+ (49, "Crash Cymbal 1"),+ (50, "High Tom 1"),+ (51, "Ride Cymbal 1"),+ (52, "Chinese Cymbal"),+ (53, "Ride Bell"),+ (54, "Tambourine"),+ (55, "Splash Cymbal"),+ (56, "Cowbell"),+ (57, "Crash Cymbal 2"),+ (58, "Vibra Slap"),+ (59, "Ride Cymbal 2"),+ (60, "High Bongo"),+ (61, "Low Bongo"),+ (62, "Mute High Conga"),+ (63, "Open High Conga"),+ (64, "Low Conga"),+ (65, "High Timbale"),+ (66, "Low Timbale"),+ (67, "High Agogô"),+ (68, "Low Agogô"),+ (69, "Cabasa"),+ (70, "Maracas"),+ (71, "Short Whistle"),+ (72, "Long Whistle"),+ (73, "Short Güiro"),+ (74, "Long Güiro"),+ (75, "Claves"),+ (76, "High Wood Block"),+ (77, "Low Wood Block"),+ (78, "Mute Cuíca"),+ (79, "Open Cuíca"),+ (80, "Mute Triangle"),+ (81, "Open Triangle")+ ]+
+ src/Music/Parts/Solo.hs view
@@ -0,0 +1,41 @@++{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-}++------------------------------------------------------------------------------------+-- |+-- Copyright : (c) Hans Hoglund 2012-2015+--+-- License : BSD-style+--+-- Maintainer : hans@hanshoglund.se+-- Stability : experimental+-- Portability : non-portable (TF,GNTD)+--+-------------------------------------------------------------------------------------++module Music.Parts.Solo (+ Solo(..),+ ) where++import Control.Applicative+import Control.Lens (toListOf)+import Data.Default+import Data.Functor.Adjunction (unzipR)+import qualified Data.List+import Data.Maybe+import Data.Semigroup+import Data.Semigroup.Option.Instances+import Data.Traversable (traverse)+import Data.Typeable+import Text.Numeral.Roman (toRoman)++data Solo+ = Solo+ | Tutti+ deriving (Eq, Show, Ord, Enum)++instance Default Solo where+ def = Tutti+
+ src/Music/Parts/Subpart.hs view
@@ -0,0 +1,55 @@++{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeFamilies #-}++------------------------------------------------------------------------------------+-- |+-- Copyright : (c) Hans Hoglund 2012-2015+--+-- License : BSD-style+--+-- Maintainer : hans@hanshoglund.se+-- Stability : experimental+-- Portability : non-portable (TF,GNTD)+--+-------------------------------------------------------------------------------------++module Music.Parts.Subpart (+ Subpart(..),+ containsSubpart,+ ) where++import Control.Applicative+import Control.Lens (toListOf)+import Data.Default+import Data.Functor.Adjunction (unzipR)+import qualified Data.List+import Data.Maybe+import Data.Semigroup+import Data.Semigroup.Option.Instances+import Data.Traversable (traverse)+import Data.Typeable+import Text.Numeral.Roman (toRoman)++import Music.Parts.Division++-- |+-- A subpart is a potentially infinite sequence of divisions, each typically+-- designated using a new index type, i.e. @I.1.2@.+--+-- The empty subpart (also known as 'mempty') represents all the players of the group,+-- or in the context of 'Part', all players of the given instrument.+--+newtype Subpart = Subpart [Division]+ deriving (Eq, Ord, Default, Semigroup, Monoid)++instance Show Subpart where+ show (Subpart ps) = Data.List.intercalate "." $ mapFR showDivisionR showDivision $ ps+ where+ mapFR f g [] = []+ mapFR f g (x:xs) = f x : fmap g xs++containsSubpart :: Subpart -> Subpart -> Bool+Subpart x `containsSubpart` Subpart y = y `Data.List.isPrefixOf` x+