about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--website/sandbox/chord-drill-sergeant/.gitignore1
-rw-r--r--website/sandbox/chord-drill-sergeant/README.md10
-rw-r--r--website/sandbox/chord-drill-sergeant/elm.json24
-rw-r--r--website/sandbox/chord-drill-sergeant/shell.nix7
-rw-r--r--website/sandbox/chord-drill-sergeant/src/Main.elm206
-rw-r--r--website/sandbox/chord-drill-sergeant/src/Piano.elm46
6 files changed, 294 insertions, 0 deletions
diff --git a/website/sandbox/chord-drill-sergeant/.gitignore b/website/sandbox/chord-drill-sergeant/.gitignore
new file mode 100644
index 000000000000..8b0d053e4e35
--- /dev/null
+++ b/website/sandbox/chord-drill-sergeant/.gitignore
@@ -0,0 +1 @@
+/elm-stuff
\ No newline at end of file
diff --git a/website/sandbox/chord-drill-sergeant/README.md b/website/sandbox/chord-drill-sergeant/README.md
index db471fa40143..0f9dd4d6bc6e 100644
--- a/website/sandbox/chord-drill-sergeant/README.md
+++ b/website/sandbox/chord-drill-sergeant/README.md
@@ -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
+```
diff --git a/website/sandbox/chord-drill-sergeant/elm.json b/website/sandbox/chord-drill-sergeant/elm.json
new file mode 100644
index 000000000000..dea3450db112
--- /dev/null
+++ b/website/sandbox/chord-drill-sergeant/elm.json
@@ -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": {}
+    }
+}
diff --git a/website/sandbox/chord-drill-sergeant/shell.nix b/website/sandbox/chord-drill-sergeant/shell.nix
new file mode 100644
index 000000000000..30061e65f9d5
--- /dev/null
+++ b/website/sandbox/chord-drill-sergeant/shell.nix
@@ -0,0 +1,7 @@
+let
+  pkgs = import <nixpkgs> {};
+in pkgs.mkShell {
+  buildInputs = with pkgs; [
+    elmPackages.elm
+  ];
+}
diff --git a/website/sandbox/chord-drill-sergeant/src/Main.elm b/website/sandbox/chord-drill-sergeant/src/Main.elm
new file mode 100644
index 000000000000..4fdb30226baf
--- /dev/null
+++ b/website/sandbox/chord-drill-sergeant/src/Main.elm
@@ -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
+           ]
diff --git a/website/sandbox/chord-drill-sergeant/src/Piano.elm b/website/sandbox/chord-drill-sergeant/src/Piano.elm
new file mode 100644
index 000000000000..7c44f4bf4f6f
--- /dev/null
+++ b/website/sandbox/chord-drill-sergeant/src/Piano.elm
@@ -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)