Model data and sketch ideas for Chord Drill Sergeant
Initialize an Elm application to build a MVP for the Chord Drill Sergeant application. There isn't much to see at the moment. I'm just sketching ideas. More forthcoming...
This commit is contained in:
parent
c350222fcc
commit
b600f709b4
6 changed files with 294 additions and 0 deletions
1
website/sandbox/chord-drill-sergeant/.gitignore
vendored
Normal file
1
website/sandbox/chord-drill-sergeant/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/elm-stuff
|
|
@ -45,3 +45,13 @@ Here are some useful features of CDS:
|
|||
- Chaos-mode: Feeling confident? Throw the classical notions of harmony to the
|
||||
wayside and use CDS in "chaos-mode" where CDS samples randomly from the Circle
|
||||
of Fifths.
|
||||
|
||||
## Developing
|
||||
|
||||
If you're interested in contributing, the following will create an environment
|
||||
in which you can develop:
|
||||
|
||||
```shell
|
||||
$ nix-shell
|
||||
$ elm reactor
|
||||
```
|
||||
|
|
24
website/sandbox/chord-drill-sergeant/elm.json
Normal file
24
website/sandbox/chord-drill-sergeant/elm.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"type": "application",
|
||||
"source-directories": [
|
||||
"src"
|
||||
],
|
||||
"elm-version": "0.19.1",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"elm/browser": "1.0.2",
|
||||
"elm/core": "1.0.5",
|
||||
"elm/html": "1.0.0"
|
||||
},
|
||||
"indirect": {
|
||||
"elm/json": "1.1.3",
|
||||
"elm/time": "1.0.0",
|
||||
"elm/url": "1.0.0",
|
||||
"elm/virtual-dom": "1.0.2"
|
||||
}
|
||||
},
|
||||
"test-dependencies": {
|
||||
"direct": {},
|
||||
"indirect": {}
|
||||
}
|
||||
}
|
7
website/sandbox/chord-drill-sergeant/shell.nix
Normal file
7
website/sandbox/chord-drill-sergeant/shell.nix
Normal file
|
@ -0,0 +1,7 @@
|
|||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
in pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
elmPackages.elm
|
||||
];
|
||||
}
|
206
website/sandbox/chord-drill-sergeant/src/Main.elm
Normal file
206
website/sandbox/chord-drill-sergeant/src/Main.elm
Normal file
|
@ -0,0 +1,206 @@
|
|||
module Main exposing (main)
|
||||
|
||||
import Browser
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (..)
|
||||
|
||||
import Piano
|
||||
|
||||
{-| 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
|
||||
|
||||
{-| 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
|
||||
|
||||
{-| 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` -}
|
||||
viewNote : Note -> String
|
||||
viewNote note =
|
||||
case note of
|
||||
C -> "C"
|
||||
C_sharp -> "C♯/D♭"
|
||||
D -> "D"
|
||||
D_sharp -> "D♯/E♭"
|
||||
E -> "E"
|
||||
F -> "F"
|
||||
F_sharp -> "F♯/G♭"
|
||||
G -> "G"
|
||||
G_sharp -> "G♯/A♭"
|
||||
A -> "A"
|
||||
A_sharp -> "A♯/B♭"
|
||||
B -> "B"
|
||||
|
||||
{-| For now, I'm just dumping things onto the page to sketch ideas. -}
|
||||
main =
|
||||
let
|
||||
key = Key (D, MinorMode)
|
||||
chord = Chord (D, Major, First)
|
||||
in
|
||||
div [] [ ul [] (notesForKey key |> List.map (\n -> li [] [ text (viewNote n) ]))
|
||||
, ul [] (notesForChord chord |> List.map (\n -> li [] [ text (viewNote n) ]))
|
||||
, Piano.render
|
||||
]
|
46
website/sandbox/chord-drill-sergeant/src/Piano.elm
Normal file
46
website/sandbox/chord-drill-sergeant/src/Piano.elm
Normal file
|
@ -0,0 +1,46 @@
|
|||
module Piano exposing (render)
|
||||
|
||||
import Browser
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (..)
|
||||
|
||||
{-| These are the white keys on most modern pianos. -}
|
||||
natural : Html a
|
||||
natural =
|
||||
li [ style "background-color" "white"
|
||||
, style "height" "20px"
|
||||
, style "border-top" "1px solid black"
|
||||
] []
|
||||
|
||||
{-| These are the black keys on most modern pianos. -}
|
||||
accidental : Html a
|
||||
accidental =
|
||||
li [ style "background-color" "black"
|
||||
, style "height" "10px"
|
||||
, style "width" "66%"
|
||||
] []
|
||||
|
||||
{-| A section of the piano consisting of all twelve notes. The name octave
|
||||
implies eight notes, which most scales (not the blues scale) honor. -}
|
||||
octave : List (Html a)
|
||||
octave = [ natural
|
||||
, accidental
|
||||
, natural
|
||||
, accidental
|
||||
, natural
|
||||
, natural
|
||||
, accidental
|
||||
, natural
|
||||
, accidental
|
||||
, natural
|
||||
, accidental
|
||||
, natural
|
||||
]
|
||||
|
||||
{-| Return the HTML that renders a piano representation. -}
|
||||
render : Html a
|
||||
render =
|
||||
ul [ style "width" "100px"
|
||||
, style "list-style" "none"
|
||||
] (octave |> List.repeat 3 |> List.concat)
|
Loading…
Reference in a new issue