module Harmonic.Framework.Builder.Core
(
TransitionSource
, offlineSource
, classicalSource
, sourceFor
, buildChainWith
, stepChainWith
, chainToProgression
, extractCadence
, matchesContextWithTarget
, applyDriftFilter
, applyPedalFilter
, resolveBassDirection
, tonalStartCue
, rootPositionCue
, directionViolations
) where
import qualified Data.Text as T
import qualified Data.IntSet as IntSet
import Control.Monad (foldM)
import Data.List (sortBy, sort, nub, isInfixOf)
import Data.Foldable (toList)
import Data.Function (on)
import Data.Ord (Down(..))
import System.Random.MWC (GenIO, uniform, uniformR, createSystemRandom)
import qualified System.Random.MWC.Distributions as Dist
import qualified Harmonic.Rules.Types.Harmony as H
import qualified Harmonic.Rules.Types.Pitch as P
import qualified Harmonic.Rules.Types.Progression as Prog
import qualified Data.Map.Strict as Map
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import qualified Data.Set as Set
import Harmonic.Database (DbConn, runDb, connectNeo4j)
import Harmonic.Evaluation.Database.Query (ComposerWeights)
import qualified Harmonic.Evaluation.Database.Query as Q
import Harmonic.Traversal.Probabilistic (gammaIndexScaledWith)
import Harmonic.Rules.Constraints.Filter (nthAbove, nthBelow)
import Harmonic.Rules.Constraints.Overtone (overtoneSets, possibleTriads)
import Harmonic.Evaluation.Scoring.Dissonance (dissonanceScore)
import qualified Harmonic.Evaluation.Scoring.Dissonance as D
import Harmonic.Framework.Builder.Types
import Harmonic.Framework.Builder.Diagnostics (computeChordTrace)
type TransitionSource = T.Text -> IO [(H.Cadence, Double)]
offlineSource :: TransitionSource
offlineSource :: TransitionSource
offlineSource Text
_ = [(Cadence, Double)] -> IO [(Cadence, Double)]
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure []
classicalSource :: DbConn -> ComposerWeights -> TransitionSource
classicalSource :: DbConn -> ComposerWeights -> TransitionSource
classicalSource DbConn
conn ComposerWeights
weights Text
key
| ComposerWeights -> Bool
forall k a. Map k a -> Bool
Map.null ComposerWeights
weights = DbConn -> DbActionT [(Cadence, Double)] -> IO [(Cadence, Double)]
forall a. DbConn -> DbActionT a -> IO a
runDb DbConn
conn (Text -> DbActionT [(Cadence, Double)]
Q.fetchTransitionsAggregate Text
key)
| Bool
otherwise = DbConn -> DbActionT [(Cadence, Double)] -> IO [(Cadence, Double)]
forall a. DbConn -> DbActionT a -> IO a
runDb DbConn
conn (ComposerWeights
-> [(Cadence, ComposerWeights)] -> [(Cadence, Double)]
forall a.
ComposerWeights -> [(a, ComposerWeights)] -> [(a, Double)]
Q.applyComposerBlend ComposerWeights
weights ([(Cadence, ComposerWeights)] -> [(Cadence, Double)])
-> ReaderT DbConn IO [(Cadence, ComposerWeights)]
-> DbActionT [(Cadence, Double)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> ReaderT DbConn IO [(Cadence, ComposerWeights)]
Q.fetchTransitions Text
key)
sourceFor :: T.Text -> IO TransitionSource
sourceFor :: Text -> IO TransitionSource
sourceFor Text
seekStr
| Text -> Text
T.toLower (Text -> Text
T.strip Text
seekStr) Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"none" = TransitionSource -> IO TransitionSource
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure TransitionSource
offlineSource
| Bool
otherwise = do
conn <- IO DbConn
connectNeo4j
let blend = Text -> ComposerWeights
Q.parseComposerWeights Text
seekStr
if Map.null blend
then pure (classicalSource conn blend)
else do
ref <- newIORef (Map.keysSet blend, 0 :: Int)
pure (spellcheckedSource ref conn blend)
spellcheckedSource :: IORef (Set.Set T.Text, Int) -> DbConn -> ComposerWeights -> TransitionSource
spellcheckedSource :: IORef (Set Text, Int)
-> DbConn -> ComposerWeights -> TransitionSource
spellcheckedSource IORef (Set Text, Int)
ref DbConn
conn ComposerWeights
blend Text
key = do
rows <- DbConn
-> ReaderT DbConn IO [(Cadence, ComposerWeights)]
-> IO [(Cadence, ComposerWeights)]
forall a. DbConn -> DbActionT a -> IO a
runDb DbConn
conn (Text -> ReaderT DbConn IO [(Cadence, ComposerWeights)]
Q.fetchTransitions Text
key)
(unseen, n) <- readIORef ref
if Set.null unseen
then pure ()
else do
let seen = [Set Text] -> Set Text
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
Set.unions [ ComposerWeights -> Set Text
forall k a. Map k a -> Set k
Map.keysSet ComposerWeights
w | (Cadence
_, ComposerWeights
w) <- [(Cadence, ComposerWeights)]
rows ]
unseen' = Set Text
unseen Set Text -> Set Text -> Set Text
forall a. Ord a => Set a -> Set a -> Set a
`Set.difference` Set Text
seen
n' = Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
if n' >= spellcheckFetches && not (Set.null unseen')
then do
putStrLn $ "⚠ seek: " ++ show (map T.unpack (Set.toList unseen'))
++ " matched no composer on any edge of the first "
++ show n' ++ " steps — check the spelling against"
++ " documents/COMPOSERS.md (walk continues; their"
++ " share contributes nothing)"
writeIORef ref (Set.empty, n')
else writeIORef ref (unseen', n')
pure (Q.applyComposerBlend blend rows)
spellcheckFetches :: Int
spellcheckFetches :: Int
spellcheckFetches = Int
3
buildChainWith :: TransitionSource
-> GenIO
-> Maybe Int
-> Double
-> HarmonicContext
-> (Int -> ParsedContext)
-> H.CadenceState
-> Int
-> IO ([H.CadenceState], [StepDiagnostic])
buildChainWith :: TransitionSource
-> GenIO
-> Maybe Int
-> Double
-> HarmonicContext
-> (Int -> ParsedContext)
-> CadenceState
-> Int
-> IO ([CadenceState], [StepDiagnostic])
buildChainWith TransitionSource
source GenIO
gen Maybe Int
mVerbosity Double
ent HarmonicContext
context Int -> ParsedContext
pctxAt CadenceState
start Int
totalSteps = do
let initCounter :: Int
initCounter = if Cadence -> Bool
H.isInversion (CadenceState -> Cadence
H.stateCadence CadenceState
start) then Int
0 else Int
1
((_current, revChain, _counter), revDiags) <-
(((CadenceState, [CadenceState], Int), [StepDiagnostic])
-> Int
-> IO ((CadenceState, [CadenceState], Int), [StepDiagnostic]))
-> ((CadenceState, [CadenceState], Int), [StepDiagnostic])
-> [Int]
-> IO ((CadenceState, [CadenceState], Int), [StepDiagnostic])
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM (\((CadenceState, [CadenceState], Int), [StepDiagnostic])
acc Int
i -> TransitionSource
-> GenIO
-> Maybe Int
-> Double
-> HarmonicContext
-> ParsedContext
-> ((CadenceState, [CadenceState], Int), [StepDiagnostic])
-> Int
-> IO ((CadenceState, [CadenceState], Int), [StepDiagnostic])
stepChainWith TransitionSource
source GenIO
gen Maybe Int
mVerbosity Double
ent HarmonicContext
context (Int -> ParsedContext
pctxAt Int
i) ((CadenceState, [CadenceState], Int), [StepDiagnostic])
acc Int
i)
((CadenceState
start, [CadenceState
start], Int
initCounter), [])
[Int
1..Int
totalSteps]
pure (reverse revChain, reverse revDiags)
stepChainWith :: TransitionSource
-> GenIO
-> Maybe Int
-> Double
-> HarmonicContext
-> ParsedContext
-> ((H.CadenceState, [H.CadenceState], Int), [StepDiagnostic])
-> Int
-> IO ((H.CadenceState, [H.CadenceState], Int), [StepDiagnostic])
stepChainWith :: TransitionSource
-> GenIO
-> Maybe Int
-> Double
-> HarmonicContext
-> ParsedContext
-> ((CadenceState, [CadenceState], Int), [StepDiagnostic])
-> Int
-> IO ((CadenceState, [CadenceState], Int), [StepDiagnostic])
stepChainWith TransitionSource
source GenIO
gen Maybe Int
mVerbosity Double
ent HarmonicContext
context ParsedContext
pctx acc :: ((CadenceState, [CadenceState], Int), [StepDiagnostic])
acc@((CadenceState
current, [CadenceState]
_, Int
_), [StepDiagnostic]
_) Int
stepNum = do
scored <- TransitionSource
source ([Char] -> Text
T.pack ([Char] -> Text) -> [Char] -> Text
forall a b. (a -> b) -> a -> b
$ Cadence -> [Char]
forall a. Show a => a -> [Char]
show (CadenceState -> Cadence
extractCadence (CadenceState -> CadenceState
H.walkTriadState CadenceState
current)))
stepChainBody gen mVerbosity ent context pctx acc stepNum scored
resolveBassDirection
:: GenIO -> Int -> Maybe BassDirectionSpec -> IO (Maybe BassDirection)
resolveBassDirection :: GenIO -> Int -> Maybe BassDirectionSpec -> IO (Maybe BassDirection)
resolveBassDirection GenIO
_ Int
_ Maybe BassDirectionSpec
Nothing = Maybe BassDirection -> IO (Maybe BassDirection)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe BassDirection
forall a. Maybe a
Nothing
resolveBassDirection GenIO
gen Int
stepNum (Just BassDirectionSpec
spec) = do
active <- if BassDirectionSpec -> Bool
bdsOptional BassDirectionSpec
spec
then do
r <- GenIO -> IO Double
forall a (m :: * -> *).
(Variate a, PrimMonad m) =>
Gen (PrimState m) -> m a
forall (m :: * -> *). PrimMonad m => Gen (PrimState m) -> m Double
uniform GenIO
gen :: IO Double
pure (r < 0.5)
else Bool -> IO Bool
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Bool
True
if not active
then pure Nothing
else do
let cs = BassDirectionSpec -> [Int]
bdsChoices BassDirectionSpec
spec
n <- case bdsSelector spec of
BDSelector
BDFixed -> Int -> IO Int
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (case [Int]
cs of { (Int
c : [Int]
_) -> Int
c; [] -> Int
1 })
BDSelector
BDRotate -> Int -> IO Int
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Int]
cs [Int] -> Int -> Int
forall a. HasCallStack => [a] -> Int -> a
!! ((Int
stepNum Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` [Int] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Int]
cs))
BDSelector
BDRandomPick -> do
i <- (Int, Int) -> GenIO -> IO Int
forall a (m :: * -> *).
(Variate a, PrimMonad m) =>
(a, a) -> Gen (PrimState m) -> m a
forall (m :: * -> *).
PrimMonad m =>
(Int, Int) -> Gen (PrimState m) -> m Int
uniformR (Int
0, [Int] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Int]
cs Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) GenIO
gen
pure (cs !! i)
pure $ Just $ case bdsKind spec of
BDKind
RiseK -> Int -> BassDirection
Rise Int
n
BDKind
FallK -> Int -> BassDirection
Fall Int
n
stepChainBody :: GenIO
-> Maybe Int
-> Double
-> HarmonicContext
-> ParsedContext
-> ((H.CadenceState, [H.CadenceState], Int), [StepDiagnostic])
-> Int
-> [(H.Cadence, Double)]
-> IO ((H.CadenceState, [H.CadenceState], Int), [StepDiagnostic])
stepChainBody :: GenIO
-> Maybe Int
-> Double
-> HarmonicContext
-> ParsedContext
-> ((CadenceState, [CadenceState], Int), [StepDiagnostic])
-> Int
-> [(Cadence, Double)]
-> IO ((CadenceState, [CadenceState], Int), [StepDiagnostic])
stepChainBody GenIO
gen Maybe Int
mVerbosity Double
ent HarmonicContext
_context ParsedContext
pctx ((CadenceState
current, [CadenceState]
revChain, Int
nonInvCount), [StepDiagnostic]
revDiags) Int
stepNum [(Cadence, Double)]
transitions = do
let walkCur :: CadenceState
walkCur = CadenceState -> CadenceState
H.walkTriadState CadenceState
current
mDir <- GenIO -> Int -> Maybe BassDirectionSpec -> IO (Maybe BassDirection)
resolveBassDirection GenIO
gen Int
stepNum (ParsedContext -> Maybe BassDirectionSpec
pcBassDirectionSpec ParsedContext
pctx)
let prevBassPC = PitchClass -> Int
P.unPitchClass (NoteName -> PitchClass
P.pitchClass (CadenceState -> NoteName
H.stateCadenceRoot CadenceState
current))
bassTarget = case Maybe BassDirection
mDir of
Maybe BassDirection
Nothing -> Maybe Int
forall a. Maybe a
Nothing
Just (Rise Int
n) -> Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> IntSet -> Int
nthAbove Int
n Int
prevBassPC (ParsedContext -> IntSet
pcAllowedBassNotes ParsedContext
pctx)
Just (Fall Int
n) -> Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int) -> Int -> Maybe Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> IntSet -> Int
nthBelow Int
n Int
prevBassPC (ParsedContext -> IntSet
pcAllowedBassNotes ParsedContext
pctx)
let filtered = Maybe Int
-> ParsedContext
-> CadenceState
-> [(Cadence, Double)]
-> [(Cadence, Double)]
forall a.
Maybe Int
-> ParsedContext
-> CadenceState
-> [(Cadence, a)]
-> [(Cadence, a)]
applyRConstraintsWithTarget Maybe Int
bassTarget ParsedContext
pctx CadenceState
walkCur [(Cadence, Double)]
transitions
let
boost = ParsedContext -> Double
pcSoftBoost ParsedContext
pctx
graphCandidates
| Double
boost Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== Double
1.0 = [(Cadence, Double)]
filtered
| Bool
otherwise =
let boosted :: [(Cadence, Double)]
boosted = [(Cadence
c, Double
s Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
boost) | (Cadence
c, Double
s) <- [(Cadence, Double)]
filtered]
in ((Cadence, Double) -> (Cadence, Double) -> Ordering)
-> [(Cadence, Double)] -> [(Cadence, Double)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (Down Double -> Down Double -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Down Double -> Down Double -> Ordering)
-> ((Cadence, Double) -> Down Double)
-> (Cadence, Double)
-> (Cadence, Double)
-> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (Double -> Down Double
forall a. a -> Down a
Down (Double -> Down Double)
-> ((Cadence, Double) -> Double)
-> (Cadence, Double)
-> Down Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cadence, Double) -> Double
forall a b. (a, b) -> b
snd)) [(Cadence, Double)]
boosted
graphCount = [(Cadence, Double)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Cadence, Double)]
graphCandidates
fallbackAll <- consonanceFallbackParsed gen walkCur pctx
let unfilteredFallback = [(Cadence
cad, Double
score) | (Cadence
cad, Double
score, Double
_, Double
_, Double
_) <- [(Cadence, Double, Double, Double, Double)]
fallbackAll]
filteredFallback = ((Cadence, Double) -> Bool)
-> [(Cadence, Double)] -> [(Cadence, Double)]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(Cadence
cad, Double
_) -> Maybe Int -> ParsedContext -> CadenceState -> Cadence -> Bool
matchesContextWithTarget Maybe Int
bassTarget ParsedContext
pctx CadenceState
walkCur Cadence
cad) [(Cadence, Double)]
unfilteredFallback
fallbackCount = [(Cadence, Double)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Cadence, Double)]
filteredFallback
pool = [(Cadence, Double)]
graphCandidates [(Cadence, Double)] -> [(Cadence, Double)] -> [(Cadence, Double)]
forall a. [a] -> [a] -> [a]
++ [(Cadence, Double)]
filteredFallback
driftedPool = Drift -> CadenceState -> [(Cadence, Double)] -> [(Cadence, Double)]
forall a. Drift -> CadenceState -> [(Cadence, a)] -> [(Cadence, a)]
applyDriftFilter (ParsedContext -> Drift
pcDrift ParsedContext
pctx) CadenceState
walkCur [(Cadence, Double)]
pool
invSpacing = ParsedContext -> Int
pcInversionSpacing ParsedContext
pctx
inversionAllowed = Int
nonInvCount Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
invSpacing
spacedPool = if Bool
inversionAllowed
then [(Cadence, Double)]
driftedPool
else ((Cadence, Double) -> Bool)
-> [(Cadence, Double)] -> [(Cadence, Double)]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool)
-> ((Cadence, Double) -> Bool) -> (Cadence, Double) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Cadence -> Bool
H.isInversion (Cadence -> Bool)
-> ((Cadence, Double) -> Cadence) -> (Cadence, Double) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cadence, Double) -> Cadence
forall a b. (a, b) -> a
fst) [(Cadence, Double)]
driftedPool
prepedalPool = if [(Cadence, Double)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Cadence, Double)]
spacedPool then [(Cadence, Double)]
driftedPool else [(Cadence, Double)]
spacedPool
finalPool = ParsedContext
-> CadenceState -> [(Cadence, Double)] -> [(Cadence, Double)]
forall a.
ParsedContext -> CadenceState -> [(Cadence, a)] -> [(Cadence, a)]
applyPedalFilter ParsedContext
pctx CadenceState
walkCur [(Cadence, Double)]
prepedalPool
if null finalPool
then do
let diags = case Maybe Int
mVerbosity of
Maybe Int
Nothing -> [StepDiagnostic]
revDiags
Just Int
_ ->
let diag :: StepDiagnostic
diag = StepDiagnostic
{ sdStepNumber :: Int
sdStepNumber = Int
stepNum
, sdPriorCadence :: [Char]
sdPriorCadence = Cadence -> [Char]
forall a. Show a => a -> [Char]
show (CadenceState -> Cadence
extractCadence CadenceState
current)
, sdPriorRoot :: [Char]
sdPriorRoot = NoteName -> [Char]
forall a. Show a => a -> [Char]
show (CadenceState -> NoteName
H.stateCadenceRoot CadenceState
current)
, sdPriorRootPC :: Int
sdPriorRootPC = PitchClass -> Int
P.unPitchClass (NoteName -> PitchClass
P.pitchClass (CadenceState -> NoteName
H.stateCadenceRoot CadenceState
current))
, sdSelectedDbIntervals :: [Char]
sdSelectedDbIntervals = [Char]
"N/A"
, sdSelectedDbMovement :: [Char]
sdSelectedDbMovement = [Char]
"N/A"
, sdSelectedDbFunctionality :: [Char]
sdSelectedDbFunctionality = [Char]
"N/A"
, sdGraphCount :: Int
sdGraphCount = Int
0
, sdGraphTop6 :: [([Char], Double)]
sdGraphTop6 = []
, sdFallbackCount :: Int
sdFallbackCount = Int
0
, sdFallbackTop6 :: [([Char], Double, Double, Double, Double)]
sdFallbackTop6 = []
, sdPoolSize :: Int
sdPoolSize = Int
0
, sdEntropyUsed :: Double
sdEntropyUsed = Double
ent
, sdGammaIndex :: Int
sdGammaIndex = -Int
1
, sdSelectedFrom :: [Char]
sdSelectedFrom = [Char]
"none (absorbing)"
, sdPosteriorRoot :: [Char]
sdPosteriorRoot = NoteName -> [Char]
forall a. Show a => a -> [Char]
show (CadenceState -> NoteName
H.stateCadenceRoot CadenceState
current)
, sdPosteriorRootPC :: Int
sdPosteriorRootPC = PitchClass -> Int
P.unPitchClass (NoteName -> PitchClass
P.pitchClass (CadenceState -> NoteName
H.stateCadenceRoot CadenceState
current))
, sdRenderedChord :: Maybe [Char]
sdRenderedChord = Maybe [Char]
forall a. Maybe a
Nothing
, sdTransformTrace :: Maybe TransformTrace
sdTransformTrace = Maybe TransformTrace
forall a. Maybe a
Nothing
, sdAdvanceTrace :: Maybe AdvanceTrace
sdAdvanceTrace = Maybe AdvanceTrace
forall a. Maybe a
Nothing
, sdTristrataIdx :: Maybe Int
sdTristrataIdx = Maybe Int
forall a. Maybe a
Nothing
, sdTristrata :: Maybe Tristrata
sdTristrata = Maybe Tristrata
forall a. Maybe a
Nothing
, sdStrataLabel :: Maybe StrataLabel
sdStrataLabel = Maybe StrataLabel
forall a. Maybe a
Nothing
, sdMode :: Maybe Mode
sdMode = Maybe Mode
forall a. Maybe a
Nothing
, sdStrataChroma :: Maybe [PitchClass]
sdStrataChroma = Maybe [PitchClass]
forall a. Maybe a
Nothing
, sdModeChroma :: Maybe [PitchClass]
sdModeChroma = Maybe [PitchClass]
forall a. Maybe a
Nothing
, sdSoftBoost :: Maybe Double
sdSoftBoost = Maybe Double
forall a. Maybe a
Nothing
, sdHarmonicRootPC :: Maybe Int
sdHarmonicRootPC = Maybe Int
forall a. Maybe a
Nothing
, sdParentKey :: Maybe (PitchClass, ScaleFamily)
sdParentKey = Maybe (PitchClass, ScaleFamily)
forall a. Maybe a
Nothing
, sdModeResult :: Maybe ModeResult
sdModeResult = Maybe ModeResult
forall a. Maybe a
Nothing
, sdBarSpelling :: Maybe EnharmonicSpelling
sdBarSpelling = Maybe EnharmonicSpelling
forall a. Maybe a
Nothing
, sdPoly :: Maybe PolyDiag
sdPoly = Maybe PolyDiag
forall a. Maybe a
Nothing
}
in StepDiagnostic
diag StepDiagnostic -> [StepDiagnostic] -> [StepDiagnostic]
forall a. a -> [a] -> [a]
: [StepDiagnostic]
revDiags
pure ((current, current : revChain, nonInvCount), diags)
else do
idx <- gammaIndexScaledWith gen ent (length finalPool)
let nextCadence = (Cadence, Double) -> Cadence
forall a b. (a, b) -> a
fst ([(Cadence, Double)]
finalPool [(Cadence, Double)] -> Int -> (Cadence, Double)
forall a. HasCallStack => [a] -> Int -> a
!! Int
idx)
(newState, advTrace) = advanceStateTraced (pcKeySpelling pctx) walkCur nextCadence
newCounter = if Cadence -> Bool
H.isInversion Cadence
nextCadence then Int
0 else Int
nonInvCount Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
let emitState = CadenceState
newState
let diags = case Maybe Int
mVerbosity of
Maybe Int
Nothing -> [StepDiagnostic]
revDiags
Just Int
verbosity ->
let finalShows :: [[Char]]
finalShows = ((Cadence, Double) -> [Char]) -> [(Cadence, Double)] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map (Cadence -> [Char]
forall a. Show a => a -> [Char]
show (Cadence -> [Char])
-> ((Cadence, Double) -> Cadence) -> (Cadence, Double) -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cadence, Double) -> Cadence
forall a b. (a, b) -> a
fst) [(Cadence, Double)]
finalPool
graphShows :: [[Char]]
graphShows = ((Cadence, Double) -> [Char]) -> [(Cadence, Double)] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map (Cadence -> [Char]
forall a. Show a => a -> [Char]
show (Cadence -> [Char])
-> ((Cadence, Double) -> Cadence) -> (Cadence, Double) -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cadence, Double) -> Cadence
forall a b. (a, b) -> a
fst) [(Cadence, Double)]
graphCandidates
selectedFrom :: [Char]
selectedFrom = if Cadence -> [Char]
forall a. Show a => a -> [Char]
show Cadence
nextCadence [Char] -> [[Char]] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [[Char]]
graphShows
then [Char]
"graph" else [Char]
"fallback"
graphTop6 :: [([Char], Double)]
graphTop6 = Int -> [([Char], Double)] -> [([Char], Double)]
forall a. Int -> [a] -> [a]
take Int
6 [([Char]
s', Double
conf) | (Cadence
cad, Double
conf) <- [(Cadence, Double)]
graphCandidates
, let s' :: [Char]
s' = Cadence -> [Char]
forall a. Show a => a -> [Char]
show Cadence
cad, [Char]
s' [Char] -> [[Char]] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [[Char]]
finalShows]
fallbackTop6' :: [([Char], Double, Double, Double, Double)]
fallbackTop6' = Int
-> [([Char], Double, Double, Double, Double)]
-> [([Char], Double, Double, Double, Double)]
forall a. Int -> [a] -> [a]
take Int
6 [([Char]
s', Double
score, Double
cd, Double
md, Double
gd) | (Cadence
cad, Double
score, Double
cd, Double
md, Double
gd) <- [(Cadence, Double, Double, Double, Double)]
fallbackAll
, let s' :: [Char]
s' = Cadence -> [Char]
forall a. Show a => a -> [Char]
show Cadence
cad, [Char]
s' [Char] -> [[Char]] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [[Char]]
finalShows]
(Maybe [Char]
renderedChord, Maybe TransformTrace
transformTrace) = Int -> CadenceState -> (Maybe [Char], Maybe TransformTrace)
computeChordTrace Int
verbosity CadenceState
emitState
priorRoot :: NoteName
priorRoot = CadenceState -> NoteName
H.stateCadenceRoot CadenceState
current
priorRootPC :: Int
priorRootPC = PitchClass -> Int
P.unPitchClass (NoteName -> PitchClass
P.pitchClass NoteName
priorRoot)
posteriorRoot :: NoteName
posteriorRoot = CadenceState -> NoteName
H.stateCadenceRoot CadenceState
emitState
posteriorRootPC :: Int
posteriorRootPC = PitchClass -> Int
P.unPitchClass (NoteName -> PitchClass
P.pitchClass NoteName
posteriorRoot)
diag :: StepDiagnostic
diag = StepDiagnostic
{ sdStepNumber :: Int
sdStepNumber = Int
stepNum
, sdPriorCadence :: [Char]
sdPriorCadence = Cadence -> [Char]
forall a. Show a => a -> [Char]
show (CadenceState -> Cadence
extractCadence CadenceState
current)
, sdPriorRoot :: [Char]
sdPriorRoot = NoteName -> [Char]
forall a. Show a => a -> [Char]
show NoteName
priorRoot
, sdPriorRootPC :: Int
sdPriorRootPC = Int
priorRootPC
, sdSelectedDbIntervals :: [Char]
sdSelectedDbIntervals = [PitchClass] -> [Char]
forall a. Show a => a -> [Char]
show (Cadence -> [PitchClass]
H.cadenceIntervals Cadence
nextCadence)
, sdSelectedDbMovement :: [Char]
sdSelectedDbMovement = Movement -> [Char]
forall a. Show a => a -> [Char]
show (Cadence -> Movement
H.cadenceMovement Cadence
nextCadence)
, sdSelectedDbFunctionality :: [Char]
sdSelectedDbFunctionality = Cadence -> [Char]
H.cadenceFunctionality Cadence
nextCadence
, sdGraphCount :: Int
sdGraphCount = Int
graphCount
, sdGraphTop6 :: [([Char], Double)]
sdGraphTop6 = [([Char], Double)]
graphTop6
, sdFallbackCount :: Int
sdFallbackCount = Int
fallbackCount
, sdFallbackTop6 :: [([Char], Double, Double, Double, Double)]
sdFallbackTop6 = [([Char], Double, Double, Double, Double)]
fallbackTop6'
, sdPoolSize :: Int
sdPoolSize = [(Cadence, Double)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Cadence, Double)]
finalPool
, sdEntropyUsed :: Double
sdEntropyUsed = Double
ent
, sdGammaIndex :: Int
sdGammaIndex = Int
idx
, sdSelectedFrom :: [Char]
sdSelectedFrom = [Char]
selectedFrom
, sdPosteriorRoot :: [Char]
sdPosteriorRoot = NoteName -> [Char]
forall a. Show a => a -> [Char]
show NoteName
posteriorRoot
, sdPosteriorRootPC :: Int
sdPosteriorRootPC = Int
posteriorRootPC
, sdRenderedChord :: Maybe [Char]
sdRenderedChord = Maybe [Char]
renderedChord
, sdTransformTrace :: Maybe TransformTrace
sdTransformTrace = Maybe TransformTrace
transformTrace
, sdAdvanceTrace :: Maybe AdvanceTrace
sdAdvanceTrace = if Int
verbosity Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
2 then AdvanceTrace -> Maybe AdvanceTrace
forall a. a -> Maybe a
Just AdvanceTrace
advTrace else Maybe AdvanceTrace
forall a. Maybe a
Nothing
, sdTristrataIdx :: Maybe Int
sdTristrataIdx = Maybe Int
forall a. Maybe a
Nothing
, sdTristrata :: Maybe Tristrata
sdTristrata = Maybe Tristrata
forall a. Maybe a
Nothing
, sdStrataLabel :: Maybe StrataLabel
sdStrataLabel = Maybe StrataLabel
forall a. Maybe a
Nothing
, sdMode :: Maybe Mode
sdMode = Maybe Mode
forall a. Maybe a
Nothing
, sdStrataChroma :: Maybe [PitchClass]
sdStrataChroma = Maybe [PitchClass]
forall a. Maybe a
Nothing
, sdModeChroma :: Maybe [PitchClass]
sdModeChroma = Maybe [PitchClass]
forall a. Maybe a
Nothing
, sdSoftBoost :: Maybe Double
sdSoftBoost = Maybe Double
forall a. Maybe a
Nothing
, sdHarmonicRootPC :: Maybe Int
sdHarmonicRootPC = Maybe Int
forall a. Maybe a
Nothing
, sdParentKey :: Maybe (PitchClass, ScaleFamily)
sdParentKey = Maybe (PitchClass, ScaleFamily)
forall a. Maybe a
Nothing
, sdModeResult :: Maybe ModeResult
sdModeResult = Maybe ModeResult
forall a. Maybe a
Nothing
, sdBarSpelling :: Maybe EnharmonicSpelling
sdBarSpelling = Maybe EnharmonicSpelling
forall a. Maybe a
Nothing
, sdPoly :: Maybe PolyDiag
sdPoly = Maybe PolyDiag
forall a. Maybe a
Nothing
}
in StepDiagnostic
diag StepDiagnostic -> [StepDiagnostic] -> [StepDiagnostic]
forall a. a -> [a] -> [a]
: [StepDiagnostic]
revDiags
pure ((emitState, emitState : revChain, newCounter), diags)
consonanceFallbackParsed :: GenIO -> H.CadenceState -> ParsedContext -> IO [(H.Cadence, Double, Double, Double, Double)]
consonanceFallbackParsed :: GenIO
-> CadenceState
-> ParsedContext
-> IO [(Cadence, Double, Double, Double, Double)]
consonanceFallbackParsed GenIO
gen CadenceState
currentState ParsedContext
pctx =
let currentRoot :: PitchClass
currentRoot = NoteName -> PitchClass
P.pitchClass (CadenceState -> NoteName
H.stateCadenceRoot CadenceState
currentState)
effectiveOvertones :: [Int]
effectiveOvertones = IntSet -> [Int]
IntSet.toList (ParsedContext -> IntSet
pcEffectiveOvertones ParsedContext
pctx)
triads :: [[Int]]
triads = (Int -> [[Int]]) -> [Int] -> [[Int]]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\Int
r -> Int -> [Int] -> [Int] -> [[Int]]
forall a. (Eq a, Ord a) => Int -> [a] -> [a] -> [[a]]
overtoneSets Int
3 [Int
r] [Int]
effectiveOvertones) [Int]
effectiveOvertones
boost :: Double
boost = ParsedContext -> Double
pcSoftBoost ParsedContext
pctx
in do
results <- ([Int] -> IO (Cadence, Double, Double, Double, Double))
-> [[Int]] -> IO [(Cadence, Double, Double, Double, Double)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (\[Int]
t -> do
let cad :: Cadence
cad = PitchClass -> [Int] -> Cadence
triadToCadenceFrom PitchClass
currentRoot [Int]
t
(score, cd, md, gd) <- GenIO
-> PitchClass
-> Cadence
-> [Int]
-> Double
-> IO (Double, Double, Double, Double)
computeFallbackScoreWithBoost GenIO
gen PitchClass
currentRoot Cadence
cad [Int]
t Double
boost
pure (cad, score, cd, md, gd)
) [[Int]]
triads
pure $ sortBy (compare `on` (\(Cadence
_, Double
s, Double
_, Double
_, Double
_) -> Double -> Down Double
forall a. a -> Down a
Down Double
s)) results
triadToCadenceFrom :: P.PitchClass -> [Int] -> H.Cadence
triadToCadenceFrom :: PitchClass -> [Int] -> Cadence
triadToCadenceFrom PitchClass
currentRoot [Int]
pitches =
let triadRoot :: PitchClass
triadRoot = Int -> PitchClass
P.mkPitchClass (case [Int]
pitches of { (Int
p : [Int]
_) -> Int
p; [] -> Int
0 })
movement :: Movement
movement = PitchClass -> PitchClass -> Movement
H.toMovement PitchClass
currentRoot PitchClass
triadRoot
pcs :: [PitchClass]
pcs = [PitchClass] -> [PitchClass]
H.zeroFormPC ((Int -> PitchClass) -> [Int] -> [PitchClass]
forall a b. (a -> b) -> [a] -> [b]
map Int -> PitchClass
P.mkPitchClass [Int]
pitches)
functionality :: [Char]
functionality = [PitchClass] -> [Char]
H.toFunctionality [PitchClass]
pcs
in [Char] -> Movement -> [PitchClass] -> Cadence
H.Cadence [Char]
functionality Movement
movement [PitchClass]
pcs
computeFallbackScoreWithBoost :: GenIO -> P.PitchClass -> H.Cadence -> [Int] -> Double -> IO (Double, Double, Double, Double)
computeFallbackScoreWithBoost :: GenIO
-> PitchClass
-> Cadence
-> [Int]
-> Double
-> IO (Double, Double, Double, Double)
computeFallbackScoreWithBoost GenIO
gen PitchClass
_currentRoot Cadence
cad [Int]
triad Double
boost = do
let chordDiss :: Double
chordDiss = Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral ([Int] -> Integer
dissonanceScore [Int]
triad) :: Double
interval :: Int
interval = Movement -> Int
extractMovementInterval (Cadence -> Movement
H.cadenceMovement Cadence
cad)
motionDiss :: Double
motionDiss = Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Integer
D.rootMotionScore Int
interval) :: Double
gammaDraw <- Double -> Double -> Gen RealWorld -> IO Double
forall g (m :: * -> *).
StatefulGen g m =>
Double -> Double -> g -> m Double
Dist.gamma Double
1.01 Double
1.0 Gen RealWorld
GenIO
gen
let badness = Double
chordDiss Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
motionDiss Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
gammaDraw Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
1.0) Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
boost
finalScore = Double
10000.0 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
badness
pure (finalScore, chordDiss, motionDiss, gammaDraw)
extractMovementInterval :: H.Movement -> Int
Movement
movement = case Movement
movement of
H.Asc PitchClass
pc -> Int -> Int
forall {a}. Integral a => a -> a
intervalClassFromPC (PitchClass -> Int
P.unPitchClass PitchClass
pc)
H.Desc PitchClass
pc -> Int -> Int
forall {a}. Integral a => a -> a
intervalClassFromPC (PitchClass -> Int
P.unPitchClass PitchClass
pc)
Movement
H.Unison -> Int
0
Movement
H.Tritone -> Int
6
Movement
H.Empty -> Int
0
where
intervalClassFromPC :: a -> a
intervalClassFromPC a
semitones =
let m :: a
m = a
semitones a -> a -> a
forall a. Integral a => a -> a -> a
`mod` a
12
in if a
m a -> a -> Bool
forall a. Ord a => a -> a -> Bool
<= a
6 then a
m else a
12 a -> a -> a
forall a. Num a => a -> a -> a
- a
m
applyDriftFilter :: Drift -> H.CadenceState -> [(H.Cadence, a)] -> [(H.Cadence, a)]
applyDriftFilter :: forall a. Drift -> CadenceState -> [(Cadence, a)] -> [(Cadence, a)]
applyDriftFilter Drift
Free CadenceState
_ [(Cadence, a)]
pool = [(Cadence, a)]
pool
applyDriftFilter Drift
direction CadenceState
currentState [(Cadence, a)]
pool =
let currentDiss :: Integer
currentDiss = [Int] -> Integer
dissonanceScore
((PitchClass -> Int) -> [PitchClass] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map PitchClass -> Int
P.unPitchClass (Cadence -> [PitchClass]
H.cadenceIntervals (CadenceState -> Cadence
H.stateCadence CadenceState
currentState)))
candidateDiss :: Cadence -> Integer
candidateDiss Cadence
cad = [Int] -> Integer
dissonanceScore ((PitchClass -> Int) -> [PitchClass] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map PitchClass -> Int
P.unPitchClass (Cadence -> [PitchClass]
H.cadenceIntervals Cadence
cad))
predicate :: (Cadence, b) -> Bool
predicate = case Drift
direction of
Drift
Dissonant -> \(Cadence
cad, b
_) -> Cadence -> Integer
candidateDiss Cadence
cad Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
currentDiss
Drift
Consonant -> \(Cadence
cad, b
_) -> Cadence -> Integer
candidateDiss Cadence
cad Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Integer
currentDiss
filtered :: [(Cadence, a)]
filtered = ((Cadence, a) -> Bool) -> [(Cadence, a)] -> [(Cadence, a)]
forall a. (a -> Bool) -> [a] -> [a]
filter (Cadence, a) -> Bool
forall {b}. (Cadence, b) -> Bool
predicate [(Cadence, a)]
pool
in if [(Cadence, a)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Cadence, a)]
filtered then [(Cadence, a)]
pool else [(Cadence, a)]
filtered
applyPedalFilter :: ParsedContext -> H.CadenceState -> [(H.Cadence, a)] -> [(H.Cadence, a)]
applyPedalFilter :: forall a.
ParsedContext -> CadenceState -> [(Cadence, a)] -> [(Cadence, a)]
applyPedalFilter ParsedContext
pctx CadenceState
currentState [(Cadence, a)]
pool
| IntSet -> Bool
IntSet.null IntSet
req Bool -> Bool -> Bool
&& IntSet -> Bool
IntSet.null IntSet
pref = [(Cadence, a)]
pool
| Bool
otherwise =
let cadenceAbsPCs :: Cadence -> IntSet
cadenceAbsPCs Cadence
cadence =
let (Movement
movement, [PitchClass]
chord) = Cadence -> (Movement, [PitchClass])
H.deconstructCadence Cadence
cadence
prevRoot :: PitchClass
prevRoot = NoteName -> PitchClass
P.pitchClass (CadenceState -> NoteName
H.stateCadenceRoot CadenceState
currentState)
newRoot :: PitchClass
newRoot = case Movement
movement of
Movement
H.Unison -> PitchClass
prevRoot
Movement
H.Tritone -> Int -> PitchClass -> PitchClass
P.transpose Int
6 PitchClass
prevRoot
H.Asc PitchClass
pc -> Int -> PitchClass -> PitchClass
P.transpose (PitchClass -> Int
P.unPitchClass PitchClass
pc) PitchClass
prevRoot
H.Desc PitchClass
pc -> Int -> PitchClass -> PitchClass
P.transpose (Int -> Int
forall a. Num a => a -> a
negate (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ PitchClass -> Int
P.unPitchClass PitchClass
pc) PitchClass
prevRoot
Movement
H.Empty -> PitchClass
prevRoot
rootInt :: Int
rootInt = Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (PitchClass -> Int
P.unPitchClass PitchClass
newRoot)
chordInts :: [Int]
chordInts = (PitchClass -> Int) -> [PitchClass] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int) -> (PitchClass -> Int) -> PitchClass -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PitchClass -> Int
P.unPitchClass) [PitchClass]
chord
in [Int] -> IntSet
IntSet.fromList ([Int] -> IntSet) -> [Int] -> IntSet
forall a b. (a -> b) -> a -> b
$ (Int -> Int) -> [Int] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (\Int
i -> (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
rootInt) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12) [Int]
chordInts
combined :: IntSet
combined = IntSet -> IntSet -> IntSet
IntSet.union IntSet
req IntSet
pref
reqFiltered :: [(Cadence, a)]
reqFiltered = ((Cadence, a) -> Bool) -> [(Cadence, a)] -> [(Cadence, a)]
forall a. (a -> Bool) -> [a] -> [a]
filter (IntSet -> IntSet -> Bool
IntSet.isSubsetOf IntSet
req (IntSet -> Bool)
-> ((Cadence, a) -> IntSet) -> (Cadence, a) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Cadence -> IntSet
cadenceAbsPCs (Cadence -> IntSet)
-> ((Cadence, a) -> Cadence) -> (Cadence, a) -> IntSet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cadence, a) -> Cadence
forall a b. (a, b) -> a
fst) [(Cadence, a)]
pool
combFiltered :: [(Cadence, a)]
combFiltered = ((Cadence, a) -> Bool) -> [(Cadence, a)] -> [(Cadence, a)]
forall a. (a -> Bool) -> [a] -> [a]
filter (IntSet -> IntSet -> Bool
IntSet.isSubsetOf IntSet
combined (IntSet -> Bool)
-> ((Cadence, a) -> IntSet) -> (Cadence, a) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Cadence -> IntSet
cadenceAbsPCs (Cadence -> IntSet)
-> ((Cadence, a) -> Cadence) -> (Cadence, a) -> IntSet
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cadence, a) -> Cadence
forall a b. (a, b) -> a
fst) [(Cadence, a)]
pool
result :: [(Cadence, a)]
result
| IntSet -> Bool
IntSet.null IntSet
pref = [(Cadence, a)]
reqFiltered
| [(Cadence, a)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Cadence, a)]
combFiltered Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
minPedalPool = [(Cadence, a)]
combFiltered
| Bool -> Bool
not ([(Cadence, a)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(Cadence, a)]
reqFiltered) = [(Cadence, a)]
reqFiltered
| Bool
otherwise = [(Cadence, a)]
pool
in [(Cadence, a)]
result
where
req :: IntSet
req = ParsedContext -> IntSet
pcPedalRequired ParsedContext
pctx
pref :: IntSet
pref = ParsedContext -> IntSet
pcPedalPreferred ParsedContext
pctx
minPedalPool :: Int
minPedalPool :: Int
minPedalPool = Int
10
applyRConstraintsWithTarget :: Maybe Int
-> ParsedContext
-> H.CadenceState
-> [(H.Cadence, a)]
-> [(H.Cadence, a)]
applyRConstraintsWithTarget :: forall a.
Maybe Int
-> ParsedContext
-> CadenceState
-> [(Cadence, a)]
-> [(Cadence, a)]
applyRConstraintsWithTarget Maybe Int
bassTarget ParsedContext
pctx CadenceState
currentState =
((Cadence, a) -> Bool) -> [(Cadence, a)] -> [(Cadence, a)]
forall a. (a -> Bool) -> [a] -> [a]
filter (Maybe Int -> ParsedContext -> CadenceState -> Cadence -> Bool
matchesContextWithTarget Maybe Int
bassTarget ParsedContext
pctx CadenceState
currentState (Cadence -> Bool)
-> ((Cadence, a) -> Cadence) -> (Cadence, a) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Cadence, a) -> Cadence
forall a b. (a, b) -> a
fst)
matchesContextWithTarget :: Maybe Int -> ParsedContext -> H.CadenceState -> H.Cadence -> Bool
matchesContextWithTarget :: Maybe Int -> ParsedContext -> CadenceState -> Cadence -> Bool
matchesContextWithTarget Maybe Int
bassTarget ParsedContext
pctx CadenceState
currentState Cadence
cadence =
let (Movement
movement, [PitchClass]
chord) = Cadence -> (Movement, [PitchClass])
H.deconstructCadence Cadence
cadence
prevRoot :: PitchClass
prevRoot = NoteName -> PitchClass
P.pitchClass (CadenceState -> NoteName
H.stateCadenceRoot CadenceState
currentState)
currentRoot :: PitchClass
currentRoot = case Movement
movement of
Movement
H.Unison -> PitchClass
prevRoot
Movement
H.Tritone -> Int -> PitchClass -> PitchClass
P.transpose Int
6 PitchClass
prevRoot
H.Asc PitchClass
pc -> Int -> PitchClass -> PitchClass
P.transpose (PitchClass -> Int
P.unPitchClass PitchClass
pc) PitchClass
prevRoot
H.Desc PitchClass
pc -> Int -> PitchClass -> PitchClass
P.transpose (Int -> Int
forall a. Num a => a -> a
negate (Int -> Int) -> Int -> Int
forall a b. (a -> b) -> a -> b
$ PitchClass -> Int
P.unPitchClass PitchClass
pc) PitchClass
prevRoot
Movement
H.Empty -> PitchClass
prevRoot
chordInts :: [Int]
chordInts = (PitchClass -> Int) -> [PitchClass] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int) -> (PitchClass -> Int) -> PitchClass -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PitchClass -> Int
P.unPitchClass) [PitchClass]
chord
currentRootInt :: Int
currentRootInt = Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (PitchClass -> Int
P.unPitchClass PitchClass
currentRoot)
absolutePitches :: [Int]
absolutePitches = (Int -> Int) -> [Int] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (\Int
interval -> (Int
interval Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
currentRootInt) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12) [Int]
chordInts
bassInt :: Int
bassInt = case [Int]
absolutePitches of { [] -> Int
0; (Int
p : [Int]
_) -> Int
p }
pitchesToCheck :: [Int]
pitchesToCheck
| ParsedContext -> Bool
pcStrictContainment ParsedContext
pctx = [Int]
absolutePitches
| Bool
otherwise = case Maybe Int
bassTarget of
Just Int
target -> (Int -> Bool) -> [Int] -> [Int]
forall a. (a -> Bool) -> [a] -> [a]
filter (Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
target) [Int]
absolutePitches
Maybe Int
Nothing -> [Int]
absolutePitches
overtonesMatch :: Bool
overtonesMatch = (Int -> Bool) -> [Int] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Int -> IntSet -> Bool
`IntSet.member` ParsedContext -> IntSet
pcEffectiveOvertones ParsedContext
pctx) [Int]
pitchesToCheck
bassMatch :: Bool
bassMatch = case Maybe Int
bassTarget of
Just Int
target -> Int
bassInt Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
target
Maybe Int
Nothing -> ParsedContext -> Bool
pcIsRootsWild ParsedContext
pctx
Bool -> Bool -> Bool
|| Int
bassInt Int -> IntSet -> Bool
`IntSet.member` ParsedContext -> IntSet
pcAllowedBassNotes ParsedContext
pctx
in Bool
overtonesMatch Bool -> Bool -> Bool
&& Bool
bassMatch
advanceStateTraced :: Maybe H.EnharmonicSpelling -> H.CadenceState -> H.Cadence -> (H.CadenceState, AdvanceTrace)
advanceStateTraced :: Maybe EnharmonicSpelling
-> CadenceState -> Cadence -> (CadenceState, AdvanceTrace)
advanceStateTraced Maybe EnharmonicSpelling
keyBias CadenceState
currentState Cadence
newCadence =
let currentRoot :: NoteName
currentRoot = CadenceState -> NoteName
H.stateCadenceRoot CadenceState
currentState
currentRootPC :: PitchClass
currentRootPC = NoteName -> PitchClass
P.pitchClass NoteName
currentRoot
movement :: Movement
movement = Cadence -> Movement
H.cadenceMovement Cadence
newCadence
movementInterval :: PitchClass
movementInterval = Movement -> PitchClass
H.fromMovement Movement
movement
newRootPC :: PitchClass
newRootPC = PitchClass
currentRootPC PitchClass -> PitchClass -> PitchClass
forall a. Num a => a -> a -> a
+ PitchClass
movementInterval
tones :: [Int]
tones = (PitchClass -> Int) -> [PitchClass] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map PitchClass -> Int
P.unPitchClass ([PitchClass] -> [Int]) -> [PitchClass] -> [Int]
forall a b. (a -> b) -> a -> b
$ Cadence -> [PitchClass]
H.cadenceIntervals Cadence
newCadence
absolutePitches :: [Int]
absolutePitches = (Int -> Int) -> [Int] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (\Int
t -> (Int
t Int -> Int -> Int
forall a. Num a => a -> a -> a
+ PitchClass -> Int
P.unPitchClass PitchClass
newRootPC) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12) [Int]
tones
inferredSpelling :: EnharmonicSpelling
inferredSpelling = [Int] -> EnharmonicSpelling
H.inferSpelling [Int]
absolutePitches
newSpelling :: EnharmonicSpelling
newSpelling = case Maybe EnharmonicSpelling
keyBias of
Just EnharmonicSpelling
ks -> EnharmonicSpelling
ks
Maybe EnharmonicSpelling
Nothing
| PitchClass
newRootPC PitchClass -> PitchClass -> Bool
forall a. Eq a => a -> a -> Bool
== PitchClass
currentRootPC -> CadenceState -> EnharmonicSpelling
H.stateSpelling CadenceState
currentState
| [Int] -> Bool
H.isAmbiguousPattern [Int]
absolutePitches -> CadenceState -> EnharmonicSpelling
H.stateSpelling CadenceState
currentState
| Bool
otherwise -> EnharmonicSpelling
inferredSpelling
newRoot :: NoteName
newRoot = EnharmonicSpelling -> PitchClass -> NoteName
H.enharmonicFunc EnharmonicSpelling
newSpelling PitchClass
newRootPC
newState :: CadenceState
newState = Cadence -> NoteName -> EnharmonicSpelling -> CadenceState
H.CadenceState Cadence
newCadence NoteName
newRoot EnharmonicSpelling
newSpelling
enharmName :: [Char]
enharmName = case EnharmonicSpelling
newSpelling of
EnharmonicSpelling
H.FlatSpelling -> [Char]
"flat"
EnharmonicSpelling
H.SharpSpelling -> [Char]
"sharp"
trace :: AdvanceTrace
trace = AdvanceTrace
{ atCurrentRoot :: [Char]
atCurrentRoot = NoteName -> [Char]
forall a. Show a => a -> [Char]
show NoteName
currentRoot
, atCurrentRootPC :: Int
atCurrentRootPC = PitchClass -> Int
P.unPitchClass PitchClass
currentRootPC
, atMovement :: [Char]
atMovement = Movement -> [Char]
forall a. Show a => a -> [Char]
show Movement
movement
, atMovementInterval :: Int
atMovementInterval = PitchClass -> Int
P.unPitchClass PitchClass
movementInterval
, atNewRootPC :: Int
atNewRootPC = PitchClass -> Int
P.unPitchClass PitchClass
newRootPC
, atEnharmFunc :: [Char]
atEnharmFunc = [Char]
enharmName
, atNewRoot :: [Char]
atNewRoot = NoteName -> [Char]
forall a. Show a => a -> [Char]
show NoteName
newRoot
}
in (CadenceState
newState, AdvanceTrace
trace)
extractCadence :: H.CadenceState -> H.Cadence
= CadenceState -> Cadence
H.stateCadence
chainToProgression :: [H.CadenceState] -> Prog.Progression
chainToProgression :: [CadenceState] -> Progression
chainToProgression = [CadenceState] -> Progression
Prog.fromCadenceStates
rootPositionCue :: H.CadenceState -> Bool
rootPositionCue :: CadenceState -> Bool
rootPositionCue CadenceState
cs
| [PitchClass] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (Cadence -> [PitchClass]
H.cadenceIntervals (CadenceState -> Cadence
H.stateCadence CadenceState
cs)) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
3 = Bool
True
| Bool
otherwise = case CadenceState -> Chord
H.fromCadenceState CadenceState
cs of
H.Chord NoteName
_ [Char]
fn [Integer]
_ -> Bool -> Bool
not ([Char]
"Inv" [Char] -> [Char] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` [Char]
fn)
tonalStartCue :: GenConfig -> IO H.CadenceState
tonalStartCue :: GenConfig -> IO CadenceState
tonalStartCue GenConfig
gc
| ParsedContext -> Bool
pcIsKeyWild ParsedContext
pctx Bool -> Bool -> Bool
&& ParsedContext -> Bool
pcIsOvertonesWild ParsedContext
pctx Bool -> Bool -> Bool
&& ParsedContext -> Bool
pcIsRootsWild ParsedContext
pctx
Bool -> Bool -> Bool
&& IntSet -> Bool
IntSet.null (ParsedContext -> IntSet
pcPedalRequired ParsedContext
pctx) = GenConfig -> IO CadenceState
_gcCue GenConfig
gc
| Bool
otherwise = do
rng <- IO (Gen RealWorld)
IO GenIO
createSystemRandom
let allowed = IntSet -> [Int]
IntSet.toList (ParsedContext -> IntSet
pcEffectiveOvertones ParsedContext
pctx)
roots | ParsedContext -> Bool
pcIsRootsWild ParsedContext
pctx = [Int]
allowed
| Bool
otherwise = IntSet -> [Int]
IntSet.toList (ParsedContext -> IntSet
pcAllowedBassNotes ParsedContext
pctx)
probe (Int
r, [Int]
ivs) =
let nm :: NoteName
nm = EnharmonicSpelling -> PitchClass -> NoteName
H.enharmonicFunc EnharmonicSpelling
H.FlatSpelling (Int -> PitchClass
P.mkPitchClass Int
r)
in Int -> [Char] -> [Int] -> CadenceState
H.initCadenceState Int
0 (NoteName -> [Char]
forall a. Show a => a -> [Char]
show NoteName
nm) [Int]
ivs
pool0 = [ (Int, [Int])
cand
| Int
r <- [Int]
roots
, [Int]
pcs <- (Int, [Int]) -> [[Int]]
possibleTriads (Int
r, [Int] -> [Int]
forall a. Eq a => [a] -> [a]
nub ([Int] -> [Int]
forall a. Ord a => [a] -> [a]
sort (Int
r Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: [Int]
allowed)))
, let cand :: (Int, [Int])
cand = (Int
r, [ (Int
p Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
r) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12 | Int
p <- [Int]
pcs ])
, CadenceState -> Bool
rootPositionCue ((Int, [Int]) -> CadenceState
probe (Int, [Int])
cand) ]
pedalOk (Int
r, t Int
ivs) =
(Int -> Bool) -> [Int] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (\Int
p -> (Int
p Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
r) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12 Int -> t Int -> Bool
forall a. Eq a => a -> t a -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` t Int
ivs)
(IntSet -> [Int]
IntSet.toList (ParsedContext -> IntSet
pcPedalRequired ParsedContext
pctx))
pool = case ((Int, [Int]) -> Bool) -> [(Int, [Int])] -> [(Int, [Int])]
forall a. (a -> Bool) -> [a] -> [a]
filter (Int, [Int]) -> Bool
forall {t :: * -> *}. Foldable t => (Int, t Int) -> Bool
pedalOk [(Int, [Int])]
pool0 of
[] -> [(Int, [Int])]
pool0
[(Int, [Int])]
ps -> [(Int, [Int])]
ps
case pool of
[] -> GenConfig -> IO CadenceState
_gcCue GenConfig
gc
[(Int, [Int])]
_ -> do
i <- (Int, Int) -> GenIO -> IO Int
forall a (m :: * -> *).
(Variate a, PrimMonad m) =>
(a, a) -> Gen (PrimState m) -> m a
forall (m :: * -> *).
PrimMonad m =>
(Int, Int) -> Gen (PrimState m) -> m Int
uniformR (Int
0, [(Int, [Int])] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(Int, [Int])]
pool Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Gen RealWorld
GenIO
rng
let (rootPC, ivs) = pool !! i
spelling = EnharmonicSpelling
-> (EnharmonicSpelling -> EnharmonicSpelling)
-> Maybe EnharmonicSpelling
-> EnharmonicSpelling
forall b a. b -> (a -> b) -> Maybe a -> b
maybe EnharmonicSpelling
H.FlatSpelling EnharmonicSpelling -> EnharmonicSpelling
forall a. a -> a
id (ParsedContext -> Maybe EnharmonicSpelling
pcKeySpelling ParsedContext
pctx)
rootName = EnharmonicSpelling -> PitchClass -> NoteName
H.enharmonicFunc EnharmonicSpelling
spelling (Int -> PitchClass
P.mkPitchClass Int
rootPC)
pure (H.initCadenceState 0 (show rootName) ivs)
where
pctx :: ParsedContext
pctx = HarmonicContext -> ParsedContext
parseContextOnce (GenConfig -> HarmonicContext
_gcTonal GenConfig
gc)
directionViolations :: ParsedContext -> Prog.Progression -> Int
directionViolations :: ParsedContext -> Progression -> Int
directionViolations ParsedContext
pctx Progression
prog =
case ParsedContext -> Maybe BassDirectionSpec
pcBassDirectionSpec ParsedContext
pctx of
Just BassDirectionSpec
spec | Bool -> Bool
not (BassDirectionSpec -> Bool
bdsOptional BassDirectionSpec
spec) ->
let movements :: [Movement]
movements = (CadenceState -> Movement) -> [CadenceState] -> [Movement]
forall a b. (a -> b) -> [a] -> [b]
map (Cadence -> Movement
H.cadenceMovement (Cadence -> Movement)
-> (CadenceState -> Cadence) -> CadenceState -> Movement
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CadenceState -> Cadence
H.stateCadence)
(Int -> [CadenceState] -> [CadenceState]
forall a. Int -> [a] -> [a]
drop Int
1 (Seq CadenceState -> [CadenceState]
forall a. Seq a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (Progression -> Seq CadenceState
Prog.unProgression Progression
prog)))
ok :: Movement -> Bool
ok Movement
m = case (BassDirectionSpec -> BDKind
bdsKind BassDirectionSpec
spec, Movement
m) of
(BDKind
FallK, H.Desc PitchClass
_) -> Bool
True
(BDKind
RiseK, H.Asc PitchClass
_) -> Bool
True
(BDKind, Movement)
_ -> Bool
False
in [Movement] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ((Movement -> Bool) -> [Movement] -> [Movement]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Movement -> Bool) -> Movement -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Movement -> Bool
ok) [Movement]
movements)
Maybe BassDirectionSpec
_ -> Int
0