Generate all known chords and display randomly selected chords
First of all, Elm's purity is beautiful. I think every language should model their error messages and develop experience after Elm. If I didn't have to download packages, I don't think I would need an internet connection to troubleshoot my program's errors. This is how helpful I find the compiler. Now that that's out of the way, here's what I've changed since we've last corresponded: - Use Elm's Browser.element to create a reactive application with state - Write a function to generate all of the chords about which CDS knows - Move some code out of Main.elm into other modules - Depend on List.Extra, Random, Random.Extra What's left: - Lots of work - Instead of clicking a button to show a new chord, use a timer - Add mobile-first styling (probably add TailwindCSS) - Persist settings in LocalStorage (and then eventually create user accounts) - Allow users to curate the list of chords they're interested in practicing - Deploy the website and dogfood it Unknowns: - How can I handle tempo? I don't expect setInterval to be enough (maybe it is)...
This commit is contained in:
parent
3dac2f10ff
commit
3562343c19
4 changed files with 342 additions and 213 deletions
|
|
@ -8,13 +8,17 @@
|
||||||
"direct": {
|
"direct": {
|
||||||
"elm/browser": "1.0.2",
|
"elm/browser": "1.0.2",
|
||||||
"elm/core": "1.0.5",
|
"elm/core": "1.0.5",
|
||||||
"elm/html": "1.0.0"
|
"elm/html": "1.0.0",
|
||||||
|
"elm/random": "1.0.0",
|
||||||
|
"elm-community/list-extra": "8.2.3",
|
||||||
|
"elm-community/random-extra": "3.1.0"
|
||||||
},
|
},
|
||||||
"indirect": {
|
"indirect": {
|
||||||
"elm/json": "1.1.3",
|
"elm/json": "1.1.3",
|
||||||
"elm/time": "1.0.0",
|
"elm/time": "1.0.0",
|
||||||
"elm/url": "1.0.0",
|
"elm/url": "1.0.0",
|
||||||
"elm/virtual-dom": "1.0.2"
|
"elm/virtual-dom": "1.0.2",
|
||||||
|
"owanturist/elm-union-find": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test-dependencies": {
|
"test-dependencies": {
|
||||||
|
|
|
||||||
|
|
@ -4,203 +4,96 @@ import Browser
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (..)
|
import Html.Events exposing (..)
|
||||||
|
import Random
|
||||||
|
import Random.List
|
||||||
|
|
||||||
import Piano
|
import Piano
|
||||||
|
import Theory
|
||||||
|
|
||||||
{-| Notes are the individuals sounds that we use to create music. Think: "do re
|
type State = State { whitelistedChords : List Theory.Chord
|
||||||
mi fa so la ti do".
|
, selectedChord : Theory.Chord
|
||||||
|
}
|
||||||
|
|
||||||
Note: Technically a "C-sharp" is also a "D-flat", but I will model accidentals
|
type Msg = NextChord
|
||||||
(i.e. sharps and flats) as sharps and represent the ambiguity when I render the
|
| NewChord Theory.Chord
|
||||||
underlying state of the application.
|
|
||||||
|
|
||||||
Note: There are "notes" like A, B, D-flat, and then there are notes like "middle
|
viewChord : Theory.Chord -> String
|
||||||
C", also denoted in scientific pitch notation as C4. I'm unsure of what to call
|
viewChord (Theory.Chord (note, chordType, chordPosition)) =
|
||||||
each of these, and my application does not model scientific pitch notation yet,
|
viewNote note ++ " " ++
|
||||||
so these non-scientific pitch denote values are "notes" for now. -}
|
(case chordType of
|
||||||
type Note = C
|
Theory.Major -> "major"
|
||||||
| C_sharp
|
Theory.Major7 -> "major 7th"
|
||||||
| D
|
Theory.MajorDominant7 -> "major dominant 7th"
|
||||||
| D_sharp
|
Theory.Minor -> "minor"
|
||||||
| E
|
Theory.Minor7 -> "minor 7th"
|
||||||
| F
|
Theory.MinorDominant7 -> "minor dominant 7th"
|
||||||
| F_sharp
|
Theory.Augmented -> "augmented"
|
||||||
| G
|
Theory.Augmented7 -> "augmented 7th"
|
||||||
| G_sharp
|
Theory.Diminished -> "diminished"
|
||||||
| A
|
Theory.Diminished7 -> "diminished 7th") ++ " " ++
|
||||||
| A_sharp
|
(case chordPosition of
|
||||||
| B
|
Theory.First -> "root position"
|
||||||
|
Theory.Second -> "2nd position"
|
||||||
{-| We create "scales" by enumerating the notes of a given key. These keys are
|
Theory.Third -> "3rd position"
|
||||||
defined by the "tonic" note and the "mode". I thought about including Ionian,
|
Theory.Fourth -> "4th position")
|
||||||
Dorian, Phrygian, etc., but in the I would like to avoid over-abstracting this
|
|
||||||
early on, so I'm going to err on the side of overly concrete until I have a
|
|
||||||
better idea of the extent of this project. -}
|
|
||||||
type Mode = BluesMode
|
|
||||||
| MajorMode
|
|
||||||
| MinorMode
|
|
||||||
|
|
||||||
{-| One can measure the difference between between notes using intervals. -}
|
|
||||||
type Interval = Half
|
|
||||||
| Whole
|
|
||||||
| MajorThird
|
|
||||||
| MinorThird
|
|
||||||
|
|
||||||
{-| Songs are written in one or more keys, which define the notes and therefore
|
|
||||||
chords that harmonize with one another. -}
|
|
||||||
type Key = Key (Note, Mode)
|
|
||||||
|
|
||||||
{-| A bundle of notes which are usually, but not necessarily harmonious. -}
|
|
||||||
type Chord = Chord (Note, ChordType, ChordPosition)
|
|
||||||
|
|
||||||
{-| On a piano, a triad can be played three ways. As a rule-of-thumb, The number
|
|
||||||
of ways a pianist can play a chord is equal to the number of notes in the chord
|
|
||||||
itself. -}
|
|
||||||
type ChordPosition = First
|
|
||||||
| Second
|
|
||||||
| Third
|
|
||||||
| Fourth
|
|
||||||
|
|
||||||
{-| Many possible chords exist. This type encodes the possibilities. I am
|
|
||||||
tempted to model these in a more "DRY" way, but I worry that this abstraction
|
|
||||||
may cause more problems than it solves. -}
|
|
||||||
type ChordType = Major
|
|
||||||
| Major7
|
|
||||||
| MajorDominant7
|
|
||||||
| Minor
|
|
||||||
| Minor7
|
|
||||||
| MinorDominant7
|
|
||||||
| Augmented
|
|
||||||
| Augmented7
|
|
||||||
| Diminished
|
|
||||||
| Diminished7
|
|
||||||
|
|
||||||
{-| Encode whether you are traversing "up" or "down" intervals -}
|
|
||||||
type StepDirection = Up | Down
|
|
||||||
|
|
||||||
{-| Return a list of steps to take away from the root note to return back to the
|
|
||||||
root note for a given mode.
|
|
||||||
-}
|
|
||||||
intervalsForMode : Mode -> List Interval
|
|
||||||
intervalsForMode mode =
|
|
||||||
case mode of
|
|
||||||
MajorMode -> [Whole, Whole, Half, Whole, Whole, Whole, Half]
|
|
||||||
MinorMode -> [Whole, Half, Whole, Whole, Half, Whole, Whole]
|
|
||||||
BluesMode -> [MinorThird, Whole, Half, Half, MinorThird]
|
|
||||||
|
|
||||||
{-| Return a list of the intervals the comprise a chord -}
|
|
||||||
intervalsForChordType : ChordType -> List Interval
|
|
||||||
intervalsForChordType chordType =
|
|
||||||
case chordType of
|
|
||||||
Major -> [MajorThird, MinorThird]
|
|
||||||
Major7 -> [MajorThird, MinorThird, MajorThird]
|
|
||||||
MajorDominant7 -> [MajorThird, MinorThird, MajorThird, MinorThird]
|
|
||||||
Minor -> [MinorThird, MajorThird]
|
|
||||||
Minor7 -> [MinorThird, MajorThird, MajorThird]
|
|
||||||
MinorDominant7 -> [MinorThird, MajorThird, MajorThird, MinorThird]
|
|
||||||
Augmented -> [MajorThird, MajorThird]
|
|
||||||
Augmented7 -> [MajorThird, MajorThird, Whole]
|
|
||||||
Diminished -> [MinorThird, MinorThird]
|
|
||||||
Diminished7 -> [MinorThird, MinorThird, MinorThird]
|
|
||||||
|
|
||||||
{-| Return the note in the direction, `dir`, away from `note` `s` intervals -}
|
|
||||||
step : StepDirection -> Interval -> Note -> Note
|
|
||||||
step dir s note =
|
|
||||||
let
|
|
||||||
doHalfStep = halfStep dir
|
|
||||||
in
|
|
||||||
case s of
|
|
||||||
Half -> doHalfStep note
|
|
||||||
Whole -> doHalfStep note |> doHalfStep
|
|
||||||
MinorThird -> doHalfStep note |> doHalfStep |> doHalfStep
|
|
||||||
MajorThird -> doHalfStep note |> doHalfStep |> doHalfStep |> doHalfStep
|
|
||||||
|
|
||||||
{-| Return the note that is one half step away from `note` in the direction,
|
|
||||||
`dir`.
|
|
||||||
-}
|
|
||||||
halfStep : StepDirection -> Note -> Note
|
|
||||||
halfStep dir note =
|
|
||||||
case (dir, note) of
|
|
||||||
-- C
|
|
||||||
(Up, C) -> C_sharp
|
|
||||||
(Down, C) -> B
|
|
||||||
-- C#
|
|
||||||
(Up, C_sharp) -> D
|
|
||||||
(Down, C_sharp) -> C
|
|
||||||
-- D
|
|
||||||
(Up, D) -> D_sharp
|
|
||||||
(Down, D) -> C_sharp
|
|
||||||
-- D_sharp
|
|
||||||
(Up, D_sharp) -> E
|
|
||||||
(Down, D_sharp) -> D
|
|
||||||
-- E
|
|
||||||
(Up, E) -> F
|
|
||||||
(Down, E) -> D_sharp
|
|
||||||
-- F
|
|
||||||
(Up, F) -> F_sharp
|
|
||||||
(Down, F) -> E
|
|
||||||
-- F#
|
|
||||||
(Up, F_sharp) -> G
|
|
||||||
(Down, F_sharp) -> F
|
|
||||||
-- G
|
|
||||||
(Up, G) -> G_sharp
|
|
||||||
(Down, G) -> F_sharp
|
|
||||||
-- G#
|
|
||||||
(Up, G_sharp) -> A
|
|
||||||
(Down, G_sharp) -> A
|
|
||||||
-- A
|
|
||||||
(Up, A) -> A_sharp
|
|
||||||
(Down, A) -> G_sharp
|
|
||||||
-- A#
|
|
||||||
(Up, A_sharp) -> B
|
|
||||||
(Down, A_sharp) -> A
|
|
||||||
-- B
|
|
||||||
(Up, B) -> C
|
|
||||||
(Down, B) -> A_sharp
|
|
||||||
|
|
||||||
{-| Returns a list of all of the notes up from a give `note` -}
|
|
||||||
applySteps : List Interval -> Note -> List Note
|
|
||||||
applySteps steps note =
|
|
||||||
case List.foldl (\s (prev, result) -> ((step Up s prev), (step Up s prev :: result))) (note, []) steps of
|
|
||||||
(_, result) -> List.reverse result
|
|
||||||
|
|
||||||
{-| Return the scale for a given `key` -}
|
|
||||||
notesForKey : Key -> List Note
|
|
||||||
notesForKey key =
|
|
||||||
case key of
|
|
||||||
Key (note, mode) -> applySteps (intervalsForKeyMode mode) note
|
|
||||||
|
|
||||||
{-| Return a list of the notes that comprise a `chord` -}
|
|
||||||
notesForChord : Chord -> List Note
|
|
||||||
notesForChord chord =
|
|
||||||
case chord of
|
|
||||||
-- TODO(wpcarro): Use the Position to rotate the chord n times
|
|
||||||
Chord (note, chordType, _) -> applySteps (intervalsForChordType chordType) note
|
|
||||||
|
|
||||||
{-| Serialize a human-readable format of `note` -}
|
{-| Serialize a human-readable format of `note` -}
|
||||||
viewNote : Note -> String
|
viewNote : Theory.Note -> String
|
||||||
viewNote note =
|
viewNote note =
|
||||||
case note of
|
case note of
|
||||||
C -> "C"
|
Theory.C -> "C"
|
||||||
C_sharp -> "C♯/D♭"
|
Theory.C_sharp -> "C♯/D♭"
|
||||||
D -> "D"
|
Theory.D -> "D"
|
||||||
D_sharp -> "D♯/E♭"
|
Theory.D_sharp -> "D♯/E♭"
|
||||||
E -> "E"
|
Theory.E -> "E"
|
||||||
F -> "F"
|
Theory.F -> "F"
|
||||||
F_sharp -> "F♯/G♭"
|
Theory.F_sharp -> "F♯/G♭"
|
||||||
G -> "G"
|
Theory.G -> "G"
|
||||||
G_sharp -> "G♯/A♭"
|
Theory.G_sharp -> "G♯/A♭"
|
||||||
A -> "A"
|
Theory.A -> "A"
|
||||||
A_sharp -> "A♯/B♭"
|
Theory.A_sharp -> "A♯/B♭"
|
||||||
B -> "B"
|
Theory.B -> "B"
|
||||||
|
|
||||||
|
cmajor : Theory.Chord
|
||||||
|
cmajor = Theory.Chord (Theory.C, Theory.Major, Theory.First)
|
||||||
|
|
||||||
|
{-| The initial state for the application. -}
|
||||||
|
initialState : State
|
||||||
|
initialState =
|
||||||
|
State { whitelistedChords = Theory.allChords
|
||||||
|
, selectedChord = cmajor
|
||||||
|
}
|
||||||
|
|
||||||
|
{-| Now that we have state, we need a function to change the state. -}
|
||||||
|
update : Msg -> State -> (State, Cmd Msg)
|
||||||
|
update msg (State {whitelistedChords, selectedChord}) =
|
||||||
|
case msg of
|
||||||
|
NewChord chord -> ( State { whitelistedChords = whitelistedChords
|
||||||
|
, selectedChord = chord
|
||||||
|
}
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
NextChord -> ( State { whitelistedChords = whitelistedChords
|
||||||
|
, selectedChord = selectedChord
|
||||||
|
}
|
||||||
|
, Random.generate (\x ->
|
||||||
|
case x of
|
||||||
|
(Just chord, _) -> NewChord chord
|
||||||
|
(Nothing, _) -> NewChord cmajor)
|
||||||
|
(Random.List.choose whitelistedChords)
|
||||||
|
)
|
||||||
|
|
||||||
|
view : State -> Html Msg
|
||||||
|
view (State {selectedChord}) =
|
||||||
|
div [] [ p [] [ text (viewChord selectedChord) ]
|
||||||
|
, button [ onClick NextChord ] [ text "Next Chord" ]
|
||||||
|
, Piano.render { highlight = [] }
|
||||||
|
]
|
||||||
|
|
||||||
{-| For now, I'm just dumping things onto the page to sketch ideas. -}
|
{-| For now, I'm just dumping things onto the page to sketch ideas. -}
|
||||||
main =
|
main =
|
||||||
let
|
Browser.element { init = \() -> (initialState, Cmd.none)
|
||||||
key = Key (D, MinorMode)
|
, subscriptions = \_ -> Sub.none
|
||||||
chord = Chord (D, Major, First)
|
, update = update
|
||||||
in
|
, view = view
|
||||||
div [] [ ul [] (notesForKey key |> List.map (\n -> li [] [ text (viewNote n) ]))
|
}
|
||||||
, ul [] (notesForChord chord |> List.map (\n -> li [] [ text (viewNote n) ]))
|
|
||||||
, Piano.render
|
|
||||||
]
|
|
||||||
|
|
|
||||||
|
|
@ -5,42 +5,65 @@ import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (..)
|
import Html.Events exposing (..)
|
||||||
|
|
||||||
|
import Theory
|
||||||
|
|
||||||
{-| These are the white keys on most modern pianos. -}
|
{-| These are the white keys on most modern pianos. -}
|
||||||
natural : Html a
|
natural : Bool -> Html a
|
||||||
natural =
|
natural isHighlit =
|
||||||
li [ style "background-color" "white"
|
li [ style "background-color" (if isHighlit then "red" else "white")
|
||||||
, style "height" "20px"
|
, style "height" "20px"
|
||||||
, style "border-top" "1px solid black"
|
, style "border-top" "1px solid black"
|
||||||
|
, style "border-bottom" "1px solid black"
|
||||||
] []
|
] []
|
||||||
|
|
||||||
{-| These are the black keys on most modern pianos. -}
|
{-| These are the black keys on most modern pianos. -}
|
||||||
accidental : Html a
|
accidental : Bool -> Html a
|
||||||
accidental =
|
accidental isHighlit =
|
||||||
li [ style "background-color" "black"
|
li [ style "background-color" (if isHighlit then "red" else "black")
|
||||||
, style "height" "10px"
|
, style "height" "10px"
|
||||||
, style "width" "66%"
|
, style "width" "66%"
|
||||||
] []
|
] []
|
||||||
|
|
||||||
{-| A section of the piano consisting of all twelve notes. The name octave
|
{-| A section of the piano consisting of all twelve notes. The name octave
|
||||||
implies eight notes, which most scales (not the blues scale) honor. -}
|
implies eight notes, which most scales (not the blues scale) honor. -}
|
||||||
octave : List (Html a)
|
octave : List Theory.Note -> List (Html a)
|
||||||
octave = [ natural
|
octave highlight =
|
||||||
, accidental
|
let
|
||||||
, natural
|
isHighlit note = List.member note highlight
|
||||||
, accidental
|
in
|
||||||
, natural
|
[ natural (isHighlit Theory.C)
|
||||||
, natural
|
, accidental (isHighlit Theory.C_sharp)
|
||||||
, accidental
|
, natural (isHighlit Theory.D)
|
||||||
, natural
|
, accidental (isHighlit Theory.D_sharp)
|
||||||
, accidental
|
, natural (isHighlit Theory.E)
|
||||||
, natural
|
, natural (isHighlit Theory.F)
|
||||||
, accidental
|
, accidental (isHighlit Theory.F_sharp)
|
||||||
, natural
|
, natural (isHighlit Theory.G)
|
||||||
]
|
, accidental (isHighlit Theory.G_sharp)
|
||||||
|
, natural (isHighlit Theory.A)
|
||||||
|
, accidental (isHighlit Theory.A_sharp)
|
||||||
|
, natural (isHighlit Theory.B)
|
||||||
|
]
|
||||||
|
|
||||||
|
indexForNote : Theory.Note -> Int
|
||||||
|
indexForNote note =
|
||||||
|
case note of
|
||||||
|
Theory.C -> 0
|
||||||
|
Theory.C_sharp -> 1
|
||||||
|
Theory.D -> 2
|
||||||
|
Theory.D_sharp -> 3
|
||||||
|
Theory.E -> 4
|
||||||
|
Theory.F -> 5
|
||||||
|
Theory.F_sharp -> 6
|
||||||
|
Theory.G -> 7
|
||||||
|
Theory.G_sharp -> 8
|
||||||
|
Theory.A -> 9
|
||||||
|
Theory.A_sharp -> 10
|
||||||
|
Theory.B -> 11
|
||||||
|
|
||||||
{-| Return the HTML that renders a piano representation. -}
|
{-| Return the HTML that renders a piano representation. -}
|
||||||
render : Html a
|
render : { highlight : List Theory.Note } -> Html a
|
||||||
render =
|
render {highlight} =
|
||||||
ul [ style "width" "100px"
|
ul [ style "width" "100px"
|
||||||
, style "list-style" "none"
|
, style "list-style" "none"
|
||||||
] (octave |> List.repeat 3 |> List.concat)
|
] (octave highlight |> List.reverse |> List.repeat 1 |> List.concat)
|
||||||
|
|
|
||||||
209
website/sandbox/chord-drill-sergeant/src/Theory.elm
Normal file
209
website/sandbox/chord-drill-sergeant/src/Theory.elm
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
module Theory exposing (..)
|
||||||
|
|
||||||
|
import List.Extra
|
||||||
|
|
||||||
|
{-| Notes are the individuals sounds that we use to create music. Think: "do re
|
||||||
|
mi fa so la ti do".
|
||||||
|
|
||||||
|
Note: Technically a "C-sharp" is also a "D-flat", but I will model accidentals
|
||||||
|
(i.e. sharps and flats) as sharps and represent the ambiguity when I render the
|
||||||
|
underlying state of the application.
|
||||||
|
|
||||||
|
Note: There are "notes" like A, B, D-flat, and then there are notes like "middle
|
||||||
|
C", also denoted in scientific pitch notation as C4. I'm unsure of what to call
|
||||||
|
each of these, and my application does not model scientific pitch notation yet,
|
||||||
|
so these non-scientific pitch denote values are "notes" for now. -}
|
||||||
|
type Note = C
|
||||||
|
| C_sharp
|
||||||
|
| D
|
||||||
|
| D_sharp
|
||||||
|
| E
|
||||||
|
| F
|
||||||
|
| F_sharp
|
||||||
|
| G
|
||||||
|
| G_sharp
|
||||||
|
| A
|
||||||
|
| A_sharp
|
||||||
|
| B
|
||||||
|
|
||||||
|
{-| Encode whether you are traversing "up" or "down" intervals -}
|
||||||
|
type StepDirection = Up | Down
|
||||||
|
|
||||||
|
{-| One can measure the difference between between notes using intervals. -}
|
||||||
|
type Interval = Half
|
||||||
|
| Whole
|
||||||
|
| MajorThird
|
||||||
|
| MinorThird
|
||||||
|
|
||||||
|
{-| A bundle of notes which are usually, but not necessarily harmonious. -}
|
||||||
|
type Chord = Chord (Note, ChordType, ChordPosition)
|
||||||
|
|
||||||
|
{-| Many possible chords exist. This type encodes the possibilities. I am
|
||||||
|
tempted to model these in a more "DRY" way, but I worry that this abstraction
|
||||||
|
may cause more problems than it solves. -}
|
||||||
|
type ChordType = Major
|
||||||
|
| Major7
|
||||||
|
| MajorDominant7
|
||||||
|
| Minor
|
||||||
|
| Minor7
|
||||||
|
| MinorDominant7
|
||||||
|
| Augmented
|
||||||
|
| Augmented7
|
||||||
|
| Diminished
|
||||||
|
| Diminished7
|
||||||
|
|
||||||
|
{-| On a piano, a triad can be played three ways. As a rule-of-thumb, The number
|
||||||
|
of ways a pianist can play a chord is equal to the number of notes in the chord
|
||||||
|
itself. -}
|
||||||
|
type ChordPosition = First
|
||||||
|
| Second
|
||||||
|
| Third
|
||||||
|
| Fourth
|
||||||
|
|
||||||
|
{-| Songs are written in one or more keys, which define the notes and therefore
|
||||||
|
chords that harmonize with one another. -}
|
||||||
|
type Key = Key (Note, Mode)
|
||||||
|
|
||||||
|
{-| We create "scales" by enumerating the notes of a given key. These keys are
|
||||||
|
defined by the "tonic" note and the "mode". I thought about including Ionian,
|
||||||
|
Dorian, Phrygian, etc., but in the I would like to avoid over-abstracting this
|
||||||
|
early on, so I'm going to err on the side of overly concrete until I have a
|
||||||
|
better idea of the extent of this project. -}
|
||||||
|
type Mode = BluesMode
|
||||||
|
| MajorMode
|
||||||
|
| MinorMode
|
||||||
|
|
||||||
|
{-| Return the note that is one half step away from `note` in the direction,
|
||||||
|
`dir`.
|
||||||
|
-}
|
||||||
|
halfStep : StepDirection -> Note -> Note
|
||||||
|
halfStep dir note =
|
||||||
|
case (dir, note) of
|
||||||
|
-- C
|
||||||
|
(Up, C) -> C_sharp
|
||||||
|
(Down, C) -> B
|
||||||
|
-- C#
|
||||||
|
(Up, C_sharp) -> D
|
||||||
|
(Down, C_sharp) -> C
|
||||||
|
-- D
|
||||||
|
(Up, D) -> D_sharp
|
||||||
|
(Down, D) -> C_sharp
|
||||||
|
-- D_sharp
|
||||||
|
(Up, D_sharp) -> E
|
||||||
|
(Down, D_sharp) -> D
|
||||||
|
-- E
|
||||||
|
(Up, E) -> F
|
||||||
|
(Down, E) -> D_sharp
|
||||||
|
-- F
|
||||||
|
(Up, F) -> F_sharp
|
||||||
|
(Down, F) -> E
|
||||||
|
-- F#
|
||||||
|
(Up, F_sharp) -> G
|
||||||
|
(Down, F_sharp) -> F
|
||||||
|
-- G
|
||||||
|
(Up, G) -> G_sharp
|
||||||
|
(Down, G) -> F_sharp
|
||||||
|
-- G#
|
||||||
|
(Up, G_sharp) -> A
|
||||||
|
(Down, G_sharp) -> A
|
||||||
|
-- A
|
||||||
|
(Up, A) -> A_sharp
|
||||||
|
(Down, A) -> G_sharp
|
||||||
|
-- A#
|
||||||
|
(Up, A_sharp) -> B
|
||||||
|
(Down, A_sharp) -> A
|
||||||
|
-- B
|
||||||
|
(Up, B) -> C
|
||||||
|
(Down, B) -> A_sharp
|
||||||
|
{-| Return a list of steps to take away from the root note to return back to the
|
||||||
|
root note for a given mode.
|
||||||
|
-}
|
||||||
|
intervalsForMode : Mode -> List Interval
|
||||||
|
intervalsForMode mode =
|
||||||
|
case mode of
|
||||||
|
MajorMode -> [Whole, Whole, Half, Whole, Whole, Whole, Half]
|
||||||
|
MinorMode -> [Whole, Half, Whole, Whole, Half, Whole, Whole]
|
||||||
|
BluesMode -> [MinorThird, Whole, Half, Half, MinorThird]
|
||||||
|
|
||||||
|
{-| Return a list of the intervals the comprise a chord -}
|
||||||
|
intervalsForChordType : ChordType -> List Interval
|
||||||
|
intervalsForChordType chordType =
|
||||||
|
case chordType of
|
||||||
|
Major -> [MajorThird, MinorThird]
|
||||||
|
Major7 -> [MajorThird, MinorThird, MajorThird]
|
||||||
|
MajorDominant7 -> [MajorThird, MinorThird, MajorThird, MinorThird]
|
||||||
|
Minor -> [MinorThird, MajorThird]
|
||||||
|
Minor7 -> [MinorThird, MajorThird, MajorThird]
|
||||||
|
MinorDominant7 -> [MinorThird, MajorThird, MajorThird, MinorThird]
|
||||||
|
Augmented -> [MajorThird, MajorThird]
|
||||||
|
Augmented7 -> [MajorThird, MajorThird, Whole]
|
||||||
|
Diminished -> [MinorThird, MinorThird]
|
||||||
|
Diminished7 -> [MinorThird, MinorThird, MinorThird]
|
||||||
|
|
||||||
|
{-| Return the note in the direction, `dir`, away from `note` `s` intervals -}
|
||||||
|
step : StepDirection -> Interval -> Note -> Note
|
||||||
|
step dir s note =
|
||||||
|
let
|
||||||
|
doHalfStep = halfStep dir
|
||||||
|
in
|
||||||
|
case s of
|
||||||
|
Half -> doHalfStep note
|
||||||
|
Whole -> doHalfStep note |> doHalfStep
|
||||||
|
MinorThird -> doHalfStep note |> doHalfStep |> doHalfStep
|
||||||
|
MajorThird -> doHalfStep note |> doHalfStep |> doHalfStep |> doHalfStep
|
||||||
|
|
||||||
|
{-| Returns a list of all of the notes up from a give `note` -}
|
||||||
|
applySteps : List Interval -> Note -> List Note
|
||||||
|
applySteps steps note =
|
||||||
|
case List.foldl (\s (prev, result) -> ((step Up s prev), (step Up s prev :: result))) (note, []) steps of
|
||||||
|
(_, result) -> List.reverse result
|
||||||
|
|
||||||
|
{-| Return a list of the notes that comprise a `chord` -}
|
||||||
|
notesForChord : Chord -> List Note
|
||||||
|
notesForChord chord =
|
||||||
|
case chord of
|
||||||
|
-- TODO(wpcarro): Use the Position to rotate the chord n times
|
||||||
|
Chord (note, chordType, _) -> note :: applySteps (intervalsForChordType chordType) note
|
||||||
|
|
||||||
|
{-| Return the scale for a given `key` -}
|
||||||
|
notesForKey : Key -> List Note
|
||||||
|
notesForKey key =
|
||||||
|
case key of
|
||||||
|
Key (note, mode) -> applySteps (intervalsForMode mode) note
|
||||||
|
|
||||||
|
{-| Return a list of all of the chords that we know about. -}
|
||||||
|
allChords : List Chord
|
||||||
|
allChords =
|
||||||
|
let notes = [ C
|
||||||
|
, C_sharp
|
||||||
|
, D
|
||||||
|
, D_sharp
|
||||||
|
, E
|
||||||
|
, F
|
||||||
|
, F_sharp
|
||||||
|
, G
|
||||||
|
, G_sharp
|
||||||
|
, A
|
||||||
|
, A_sharp
|
||||||
|
, B
|
||||||
|
]
|
||||||
|
chordTypes = [ Major
|
||||||
|
, Major7
|
||||||
|
, MajorDominant7
|
||||||
|
, Minor
|
||||||
|
, Minor7
|
||||||
|
, MinorDominant7
|
||||||
|
, Augmented
|
||||||
|
, Augmented7
|
||||||
|
, Diminished
|
||||||
|
, Diminished7
|
||||||
|
]
|
||||||
|
chordPositions = [ First
|
||||||
|
, Second
|
||||||
|
, Third
|
||||||
|
, Fourth
|
||||||
|
] in
|
||||||
|
notes
|
||||||
|
|> List.Extra.andThen (\note -> chordTypes
|
||||||
|
|> List.Extra.andThen (\chordType -> chordPositions
|
||||||
|
|> List.Extra.andThen (\chordPosition -> [Chord (note, chordType, chordPosition)])))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue