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 | - 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 |   wayside and use CDS in "chaos-mode" where CDS samples randomly from the Circle | ||||||
|   of Fifths. |   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…
	
	Add table
		Add a link
		
	
		Reference in a new issue