moduleNeck.Fingering(fingering,FingeringF,Fingering)whereimport Control.Monad.Readerimport Preludeimport Partial.Unsafe(unsafeCrashWith)import Data.Argonaut.Decodeimport Data.Array(catMaybes,foldM,index,mapMaybe,zipWith,(..),filter)import Data.Foldable(intercalate,null,length)import Data.FunctorWithIndeximport Data.Function(on)import Data.Int(toNumber,ceil)import Data.Maybeimport Data.Ringimport Data.Semigroup.Foldable(classFoldable1,foldMap1,fold1)import Data.Traversableimport Data.Tupleimport Debug.Traceimport Neck.Fretimport Music.Transpose(classTranspose,trans)newtypeFingeringFa=Fingering{e4::a,b3::a,g3::a,d3::a,a2::a,e2::a}instanceeq_chord::(Eqa)=>Eq(FingeringFa)whereeq=eq`on`toArrayinstanceord_chord::(Orda)=>Ord(FingeringFa)wherecompare=compare`on`toArrayinstancefunctor_chord::FunctorFingeringFwheremapf(Fingeringx)=fingeringf(fx.e4)(fx.b3)(fx.g3)(fx.d3)(fx.a2)(fx.e2)instancefunctor_with_index_chord::FunctorWithIndexIntFingeringFwheremapWithIndexf(Fingeringx)=fingeringf(f0x.e4)(f1x.b3)(f2x.g3)(f3x.d3)(f4x.a2)(f5x.e2)instanceapply_chord::ApplyFingeringFwhereapply(Fingeringf)(Fingeringx)=fingeringf(f.e4x.e4)(f.b3x.b3)(f.g3x.g3)(f.d3x.d3)(f.a2x.a2)(f.e2x.e2)instanceapplicative_chord::ApplicativeFingeringFwherepurex=fingeringfxxxxxxinstancefoldable_chord::FoldableFingeringFwherefoldlfzchord=foldlfz$toArraychordfoldrfzchord=foldrfz$toArraychordfoldMapfchord=foldMapf$toArraychordinstancetraversable_chord::TraversableFingeringFwheretraversef(Fingeringx)=fingeringf<$>fx.e4<*>fx.b3<*>fx.g3<*>fx.d3<*>fx.a2<*>fx.e2sequence=traverseidentityinstancefoldable1_chord::Foldable1FingeringFwherefoldMap1f(Fingeringc)=fc.e4<>fc.b3<>fc.g3<>fc.d3<>fc.a2<>fc.e2fold1=foldMap1identityfingeringf::foralla.a->a->a->a->a->a->FingeringFafingeringfe4b3g3d3a2e2=Fingering{e4,b3,g3,d3,a2,e2}typeFingering=FingeringFGuitarStringinstanceshow_fingering::Show(FingeringFGuitarString)whereshow=intercalate"-"<<<map(maybe"x"show<<<unfret)<<<toArrayinstancetranspose_fingering::Transpose(FingeringFGuitarString)wheretransnfing=normaliseFingering$map(transn)fingfingering::GuitarString->GuitarString->GuitarString->GuitarString->GuitarString->GuitarString->Fingeringfingeringe4b3g3d3a2e2=iflength(filterisFretted[e4,b3,g3,d3,a2,e2])>=1thenfingeringfe4b3g3d3a2e2elsefingeringErrorfingeringError::foralla.afingeringError=unsafeCrashWith"All fingerings must have have at least 1 note note (a zero note fingering is not really a fingering)"{- The fret range of a fingering is the total span of the non-muted notes. -}fretRange::Fingering->TupleIntIntfretRangefing=fromMaybefingeringError(Tuple<$>minimumfrets<*>maximumfrets)wherefrets=catMaybes(mapunfret$toArrayfing){-
A fingering shifted 12 frets (1 octave) is essentially the same fingering. Normalisation shifts a fingering down by the maximum number of octaves, as long as there are no negative frets produced.
normaliseFingering "x-0-2-2-1-0" = "x-0-2-2-1-0"
normaliseFingering "x-15-17-17-17-15" = "x-3-5-5-5-3"
-}normaliseFingering::Fingering->FingeringnormaliseFingeringfing=map(trans(12*octaveShift))fingwhereoctaveShift=ceil(-toNumberminFret/12.0)minFret=fst$fretRangefingtoArray::FingeringF~>ArraytoArray(Fingeringc)=[c.e4,c.b3,c.g3,c.d3,c.a2,c.e2]{- A fingering is open if it contains at least one note freted at fret 0 -}isOpen::Fingering->BooleanisOpen=any$case_ofMuted->falseFretn->n==0{- A fingering is valid if all fretted notes are in the range [0, 24] -}validFingering::Fingering->BooleanvalidFingering=all$case_ofMuted->trueFretn->0<=n&&n<=24