A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add equalizer

+705 -23
+1
elm.json
··· 33 33 "justgage/tachyons-elm": "4.1.3", 34 34 "ktonon/elm-crypto": "1.1.2", 35 35 "ktonon/elm-word": "2.1.2", 36 + "mpizenberg/elm-pointer-events": "4.0.0", 36 37 "noahzgordon/elm-color-extra": "1.0.2", 37 38 "rtfeldman/elm-css": "16.0.0", 38 39 "rtfeldman/elm-hex": "1.0.0",
+29 -1
src/Applications/UI.elm
··· 14 14 import File 15 15 import File.Download 16 16 import File.Select 17 + import Html.Events.Extra.Pointer as Pointer 17 18 import Html.Styled as Html exposing (Html, div, section, text, toUnstyled) 18 19 import Html.Styled.Attributes exposing (id, style) 19 20 import Html.Styled.Lazy as Lazy ··· 33 34 import UI.Backdrop as Backdrop 34 35 import UI.Console 35 36 import UI.Core as Core exposing (Flags, Model, Msg(..)) 37 + import UI.Equalizer as Equalizer 36 38 import UI.Kit 37 39 import UI.Navigation as Navigation 38 40 import UI.Notifications ··· 98 100 ----------- 99 101 , authentication = Authentication.initialModel 100 102 , backdrop = Backdrop.initialModel 103 + , equalizer = Equalizer.initialModel 101 104 , queue = Queue.initialModel 102 105 , sources = Sources.initialModel 103 106 , tracks = Tracks.initialModel ··· 275 278 , update = Backdrop.update 276 279 } 277 280 { model = model.backdrop 281 + , msg = sub 282 + } 283 + 284 + EqualizerMsg sub -> 285 + updateChild 286 + { mapCmd = EqualizerMsg 287 + , mapModel = \child -> { model | equalizer = child } 288 + , update = Equalizer.update 289 + } 290 + { model = model.equalizer 278 291 , msg = sub 279 292 } 280 293 ··· 619 632 body : Model -> Html Msg 620 633 body model = 621 634 section 622 - [] 635 + (case model.equalizer.activeKnob of 636 + Just _ -> 637 + [ (EqualizerMsg << Equalizer.AdjustKnob) 638 + |> Pointer.onMove 639 + |> Html.Styled.Attributes.fromUnstyled 640 + , (EqualizerMsg << Equalizer.DeactivateKnob) 641 + |> Pointer.onUp 642 + |> Html.Styled.Attributes.fromUnstyled 643 + ] 644 + 645 + Nothing -> 646 + [] 647 + ) 623 648 [ Css.Global.global globalCss 624 649 625 650 ----------------------------------------- ··· 676 701 -- Pages 677 702 -------- 678 703 , case model.page of 704 + Page.Equalizer -> 705 + Html.map EqualizerMsg (Equalizer.view model.equalizer) 706 + 679 707 Page.Index -> 680 708 nothing 681 709
+3
src/Applications/UI/Core.elm
··· 11 11 import Time 12 12 import UI.Authentication as Authentication 13 13 import UI.Backdrop as Backdrop 14 + import UI.Equalizer as Equalizer 14 15 import UI.Page exposing (Page) 15 16 import UI.Queue.Core as Queue 16 17 import UI.Sources as Sources ··· 55 56 ----------------------------------------- 56 57 , authentication : Authentication.Model 57 58 , backdrop : Backdrop.Model 59 + , equalizer : Equalizer.Model 58 60 , queue : Queue.Model 59 61 , sources : Sources.Model 60 62 , tracks : Tracks.Model ··· 102 104 ----------------------------------------- 103 105 | AuthenticationMsg Authentication.Msg 104 106 | BackdropMsg Backdrop.Msg 107 + | EqualizerMsg Equalizer.Msg 105 108 | QueueMsg Queue.Msg 106 109 | SourcesMsg Sources.Msg 107 110 | TracksMsg Tracks.Msg
+523
src/Applications/UI/Equalizer.elm
··· 1 + module UI.Equalizer exposing (Model, Msg(..), adjustAllKnobs, initialModel, update, view) 2 + 3 + import Chunky exposing (..) 4 + import Color exposing (Color) 5 + import Color.Ext as Color 6 + import Css 7 + import Equalizer exposing (..) 8 + import Html.Events.Extra.Pointer as Pointer 9 + import Html.Styled exposing (Html, text) 10 + import Html.Styled.Attributes exposing (css, style) 11 + import Html.Styled.Events 12 + import Material.Icons.Navigation as Icons 13 + import Replying exposing (R3D3) 14 + import Svg.Styled exposing (Svg, polygon, svg) 15 + import Svg.Styled.Attributes 16 + import Tachyons.Classes as T 17 + import UI.Kit 18 + import UI.Navigation exposing (..) 19 + import UI.Page 20 + import UI.Ports as Ports 21 + import UI.Reply exposing (Reply) 22 + 23 + 24 + 25 + -- 🌳 26 + 27 + 28 + type Knob 29 + = Low 30 + | Mid 31 + | High 32 + | Volume 33 + 34 + 35 + type alias Model = 36 + { low : Float 37 + , mid : Float 38 + , high : Float 39 + , volume : Float 40 + 41 + -- 42 + , activeKnob : Maybe Knob 43 + , startCoordinates : { x : Float, y : Float } 44 + } 45 + 46 + 47 + initialModel : Model 48 + initialModel = 49 + { low = defaultSettings.low 50 + , mid = defaultSettings.mid 51 + , high = defaultSettings.high 52 + , volume = defaultSettings.volume 53 + 54 + -- Knob interactions 55 + -------------------- 56 + , activeKnob = Nothing 57 + , startCoordinates = { x = 0, y = 0 } 58 + } 59 + 60 + 61 + 62 + -- 📣 63 + 64 + 65 + type Msg 66 + = ActivateKnob Knob Pointer.Event 67 + | AdjustKnob Pointer.Event 68 + | DeactivateKnob Pointer.Event 69 + | ResetKnob Knob 70 + 71 + 72 + update : Msg -> Model -> R3D3 Model Msg Reply 73 + update msg model = 74 + case msg of 75 + ----------------------------------------- 76 + -- Activate 77 + ----------------------------------------- 78 + ActivateKnob theKnob { pointer } -> 79 + -- TODO: mousePos should be the center position of the knob, 80 + -- not the starting mouse position 81 + let 82 + ( x, y ) = 83 + pointer.clientPos 84 + in 85 + ( { model | activeKnob = Just theKnob, startCoordinates = { x = x, y = y } } 86 + , Cmd.none 87 + , Nothing 88 + ) 89 + 90 + ----------------------------------------- 91 + -- Adjust 92 + ----------------------------------------- 93 + AdjustKnob { pointer } -> 94 + let 95 + start = 96 + model.startCoordinates 97 + 98 + end = 99 + (\( a, b ) -> { x = a, y = b }) 100 + pointer.clientPos 101 + 102 + x = 103 + end.x - start.x 104 + 105 + y = 106 + start.y - end.y 107 + 108 + distance = 109 + sqrt (x ^ 2 + y ^ 2) 110 + 111 + angle = 112 + atan2 x y 113 + * (180 / pi) 114 + |> max (maxAngle * -1) 115 + |> min maxAngle 116 + 117 + value = 118 + case model.activeKnob of 119 + Just Volume -> 120 + Just ( Volume, (maxAngle + angle) / (maxAngle * 2) ) 121 + 122 + Just knobType -> 123 + Just ( knobType, angle / maxAngle ) 124 + 125 + Nothing -> 126 + Nothing 127 + 128 + newModel = 129 + case value of 130 + Just ( Low, v ) -> 131 + { model | low = v } 132 + 133 + Just ( Mid, v ) -> 134 + { model | mid = v } 135 + 136 + Just ( High, v ) -> 137 + { model | high = v } 138 + 139 + Just ( Volume, v ) -> 140 + { model | volume = v } 141 + 142 + Nothing -> 143 + model 144 + in 145 + case value of 146 + Just ( knobType, v ) -> 147 + ( newModel 148 + , adjustKnob knobType v 149 + , Nothing 150 + ) 151 + 152 + Nothing -> 153 + ( newModel 154 + , Cmd.none 155 + , Nothing 156 + ) 157 + 158 + ----------------------------------------- 159 + -- Deactivate 160 + ----------------------------------------- 161 + DeactivateKnob _ -> 162 + ( { model | activeKnob = Nothing } 163 + , Cmd.none 164 + , Just [ UI.Reply.SaveEnclosedUserData ] 165 + ) 166 + 167 + ----------------------------------------- 168 + -- Reset 169 + ----------------------------------------- 170 + ResetKnob Low -> 171 + reset { model | low = defaultSettings.low } Low defaultSettings.low 172 + 173 + ResetKnob Mid -> 174 + reset { model | mid = defaultSettings.mid } Mid defaultSettings.mid 175 + 176 + ResetKnob High -> 177 + reset { model | high = defaultSettings.high } High defaultSettings.high 178 + 179 + ResetKnob Volume -> 180 + reset { model | volume = defaultSettings.volume } Volume defaultSettings.volume 181 + 182 + 183 + 184 + -- 📣 ░░ KNOBS & THINGS 185 + 186 + 187 + adjustKnob : Knob -> Float -> Cmd Msg 188 + adjustKnob knobType value = 189 + Ports.adjustEqualizerSetting 190 + { value = value 191 + , knob = 192 + case knobType of 193 + Low -> 194 + "LOW" 195 + 196 + Mid -> 197 + "MID" 198 + 199 + High -> 200 + "HIGH" 201 + 202 + Volume -> 203 + "VOLUME" 204 + } 205 + 206 + 207 + adjustAllKnobs : Model -> Cmd Msg 208 + adjustAllKnobs model = 209 + Cmd.batch 210 + [ adjustKnob Low model.low 211 + , adjustKnob Mid model.mid 212 + , adjustKnob High model.high 213 + , adjustKnob Volume model.volume 214 + ] 215 + 216 + 217 + reset : Model -> Knob -> Float -> R3D3 Model Msg Reply 218 + reset newModel knobType value = 219 + ( newModel 220 + , adjustKnob knobType value 221 + , Just [ UI.Reply.SaveEnclosedUserData ] 222 + ) 223 + 224 + 225 + 226 + -- 🗺 227 + 228 + 229 + view : Model -> Html Msg 230 + view model = 231 + UI.Kit.receptacle 232 + [ ----------------------------------------- 233 + -- Navigation 234 + ----------------------------------------- 235 + UI.Navigation.local 236 + [ ( Icon Icons.arrow_back 237 + , Label "Back to list" Hidden 238 + , GoToPage UI.Page.Index 239 + ) 240 + ] 241 + 242 + ----------------------------------------- 243 + -- Content 244 + ----------------------------------------- 245 + , chunk 246 + [ T.relative ] 247 + [ chunk 248 + [ T.absolute, T.left_0, T.top_0 ] 249 + [ UI.Kit.canister [ UI.Kit.h1 "Equalizer" ] 250 + ] 251 + ] 252 + 253 + -- 254 + , UI.Kit.centeredContent 255 + [ eqView model ] 256 + ] 257 + 258 + 259 + eqView : Model -> Html Msg 260 + eqView model = 261 + chunk 262 + [ T.tc ] 263 + [ brick 264 + [ css groupStyles ] 265 + [ T.ba, T.br2, T.flex ] 266 + [ knob Volume model.volume 267 + ] 268 + 269 + -- 270 + , brick 271 + [ css groupStyles ] 272 + [ T.ba, T.br2, T.flex, T.mt3 ] 273 + [ knob Low model.low 274 + , knob Mid model.mid 275 + , knob High model.high 276 + ] 277 + ] 278 + 279 + 280 + 281 + -- KNOB 282 + 283 + 284 + knob : Knob -> Float -> Html Msg 285 + knob knobType value = 286 + brick 287 + [ css columnStyles ] 288 + [ T.flex_grow_1, T.flex_shrink_0, T.ph5, T.pv3 ] 289 + [ knob_ knobType value 290 + , knobLines 291 + , knobLabel knobType 292 + ] 293 + 294 + 295 + knob_ : Knob -> Float -> Html Msg 296 + knob_ knobType value = 297 + let 298 + angle = 299 + case knobType of 300 + Volume -> 301 + (value * maxAngle * 2) - maxAngle 302 + 303 + _ -> 304 + value * maxAngle 305 + in 306 + brick 307 + [ css knobStyles 308 + 309 + -- 310 + , [ "rotate(", String.fromFloat angle, "deg)" ] 311 + |> String.concat 312 + |> style "transform" 313 + 314 + -- 315 + , Html.Styled.Events.onDoubleClick (ResetKnob knobType) 316 + , Html.Styled.Attributes.fromUnstyled <| Pointer.onDown (ActivateKnob knobType) 317 + ] 318 + [ T.br_100, T.center, T.pointer, T.relative ] 319 + [ Html.Styled.map never knob__ ] 320 + 321 + 322 + knob__ : Html Never 323 + knob__ = 324 + raw 325 + [ decagonSvg 326 + , brick 327 + [ css layerAStyles ] 328 + [ T.absolute, T.absolute__fill, T.br_100, T.z_1 ] 329 + [ brick 330 + [ css layerBStyles ] 331 + [ T.center ] 332 + [] 333 + ] 334 + ] 335 + 336 + 337 + knobLabel : Knob -> Html Msg 338 + knobLabel knobType = 339 + brick 340 + [ css knobLabelStyles ] 341 + [ T.fw6, T.mt3, T.o_70 ] 342 + [ case knobType of 343 + Low -> 344 + text "LOW" 345 + 346 + Mid -> 347 + text "MID" 348 + 349 + High -> 350 + text "HIGH" 351 + 352 + Volume -> 353 + text "VOLUME" 354 + ] 355 + 356 + 357 + knobLines : Html Msg 358 + knobLines = 359 + brick 360 + [ css knobLineContainerStyles ] 361 + [ T.center, T.relative ] 362 + [ brick 363 + [ css (knobLineStyles 45) ] 364 + [ T.absolute, T.left_0, T.top_0 ] 365 + [] 366 + , brick 367 + [ css (knobLineStyles -45) ] 368 + [ T.absolute, T.right_0, T.top_0 ] 369 + [] 370 + ] 371 + 372 + 373 + 374 + -- VARIABLES 375 + 376 + 377 + borderColor : Css.Color 378 + borderColor = 379 + Css.rgba 0 0 0 0.075 380 + 381 + 382 + knobColor : Color 383 + knobColor = 384 + UI.Kit.colorKit.base03 385 + 386 + 387 + knobOpacity : Float 388 + knobOpacity = 389 + 0.7 390 + 391 + 392 + knobSize : Float 393 + knobSize = 394 + 36 395 + 396 + 397 + maxAngle : Float 398 + maxAngle = 399 + 135 400 + 401 + 402 + 403 + -- 🖼 404 + 405 + 406 + columnStyles : List Css.Style 407 + columnStyles = 408 + [ Css.borderRight3 (Css.px 1) Css.solid borderColor 409 + , Css.lastChild [ Css.borderRightWidth Css.zero ] 410 + ] 411 + 412 + 413 + groupStyles : List Css.Style 414 + groupStyles = 415 + [ Css.borderColor borderColor ] 416 + 417 + 418 + knobStyles : List Css.Style 419 + knobStyles = 420 + [ Css.height (Css.px knobSize) 421 + , Css.width (Css.px knobSize) 422 + 423 + -- 424 + , Css.boxShadow6 425 + Css.inset 426 + Css.zero 427 + Css.zero 428 + (Css.px 5) 429 + (Css.px 1) 430 + (knobColor 431 + |> Color.setOpacity (knobOpacity - 0.35) 432 + |> Color.toElmCssColor 433 + ) 434 + ] 435 + 436 + 437 + knobLabelStyles : List Css.Style 438 + knobLabelStyles = 439 + [ Css.fontSize (Css.px 9.5) 440 + , Css.letterSpacing (Css.em 0.025) 441 + ] 442 + 443 + 444 + knobLineContainerStyles : List Css.Style 445 + knobLineContainerStyles = 446 + [ Css.top (Css.px <| knobSize / -4.5) 447 + , Css.width (Css.px knobSize) 448 + ] 449 + 450 + 451 + knobLineStyles : Float -> List Css.Style 452 + knobLineStyles rotation = 453 + [ Css.height (Css.px 9) 454 + , Css.transform (Css.rotate <| Css.deg rotation) 455 + , Css.width (Css.px 1) 456 + 457 + -- 458 + , knobColor 459 + |> Color.setOpacity (knobOpacity + 0.1) 460 + |> Color.toElmCssColor 461 + |> Css.backgroundColor 462 + ] 463 + 464 + 465 + layerAStyles : List Css.Style 466 + layerAStyles = 467 + [ Css.margin (Css.px 8) 468 + 469 + -- 470 + , Css.boxShadow5 Css.zero 471 + Css.zero 472 + (Css.px 6) 473 + (Css.px 1) 474 + (knobColor 475 + |> Color.setOpacity (knobOpacity + 0.3) 476 + |> Color.toElmCssColor 477 + ) 478 + ] 479 + 480 + 481 + layerBStyles : List Css.Style 482 + layerBStyles = 483 + [ Css.height (Css.px 9) 484 + , Css.width (Css.px 2) 485 + 486 + -- 487 + , knobColor 488 + |> Color.setOpacity (knobOpacity + 0.1) 489 + |> Color.toElmCssColor 490 + |> Css.backgroundColor 491 + ] 492 + 493 + 494 + 495 + -- DECAGON 496 + 497 + 498 + decagonSvg : Svg msg 499 + decagonSvg = 500 + svg 501 + [ Svg.Styled.Attributes.css decagonStyles 502 + , Svg.Styled.Attributes.fill "transparent" 503 + , Svg.Styled.Attributes.height "200" 504 + , Svg.Styled.Attributes.stroke (knobColor |> Color.setOpacity knobOpacity |> Color.toCssString) 505 + , Svg.Styled.Attributes.strokeLinejoin "miter" 506 + , Svg.Styled.Attributes.strokeWidth "7px" 507 + , Svg.Styled.Attributes.viewBox "0 0 200 200" 508 + , Svg.Styled.Attributes.width "200" 509 + ] 510 + [ polygon 511 + [ Svg.Styled.Attributes.points "129.665631459995,191.301425564335 70.3343685400051,191.301425564335 22.3343685400051,156.427384220077 4,100 22.334368540005,43.5726157799226 70.334368540005,8.69857443566526 129.665631459995,8.69857443566525 177.665631459995,43.5726157799226 196,100 177.665631459995,156.427384220077" ] 512 + [] 513 + ] 514 + 515 + 516 + decagonStyles : List Css.Style 517 + decagonStyles = 518 + [ Css.height <| Css.calc (Css.pct 100) Css.minus (Css.px 8) 519 + , Css.width <| Css.calc (Css.pct 100) Css.minus (Css.px 8) 520 + 521 + -- 522 + , Css.marginTop (Css.px 4) 523 + ]
+3 -2
src/Applications/UI/Kit.elm
··· 257 257 , T.overflow_hidden 258 258 , T.relative 259 259 ] 260 - [ logoBackdrop 260 + [ Html.map never logoBackdrop 261 261 , chunk 262 262 [ T.flex 263 263 , T.flex_column ··· 363 363 [ Html.text params.label ] 364 364 365 365 366 - logoBackdrop : Html msg 366 + logoBackdrop : Html Never 367 367 logoBackdrop = 368 368 brick 369 369 [ css logoBackdropStyles ] ··· 484 484 [ Css.backgroundColor (Color.toElmCssColor colorKit.base06) 485 485 , Css.fontSize (px 11.25) 486 486 , Css.letterSpacing (px 0.25) 487 + , Css.pointerEvents Css.none 487 488 , Css.top (px -1) 488 489 ] 489 490
+11 -1
src/Applications/UI/Page.elm
··· 12 12 13 13 14 14 type Page 15 - = Index 15 + = Equalizer 16 + | Index 16 17 | Queue Queue.Page 17 18 | Settings Settings.Page 18 19 | Sources Sources.Page ··· 30 31 toString : Page -> String 31 32 toString page = 32 33 case page of 34 + Equalizer -> 35 + "/equalizer" 36 + 33 37 Index -> 34 38 "/" 35 39 ··· 75 79 ( Sources _, Sources _ ) -> 76 80 True 77 81 82 + ( Index, Equalizer ) -> 83 + True 84 + 78 85 _ -> 79 86 a == b 80 87 ··· 87 94 route = 88 95 oneOf 89 96 [ map Index top 97 + 98 + -- 99 + , map Equalizer (s "equalizer") 90 100 91 101 ----------------------------------------- 92 102 -- Queue
+4 -1
src/Applications/UI/Ports.elm
··· 1 - port module UI.Ports exposing (activeQueueItemChanged, activeQueueItemEnded, fromAlien, giveBrain, nudgeBrain, pause, play, seek, setAudioDuration, setAudioHasStalled, setAudioIsLoading, setAudioIsPlaying, toBrain, toggleRepeat, unstall) 1 + port module UI.Ports exposing (activeQueueItemChanged, activeQueueItemEnded, adjustEqualizerSetting, fromAlien, giveBrain, nudgeBrain, pause, play, seek, setAudioDuration, setAudioHasStalled, setAudioIsLoading, setAudioIsPlaying, toBrain, toggleRepeat, unstall) 2 2 3 3 import Alien 4 4 import Json.Encode as Json ··· 10 10 11 11 12 12 port activeQueueItemChanged : Maybe Queue.EngineItem -> Cmd msg 13 + 14 + 15 + port adjustEqualizerSetting : { knob : String, value : Float } -> Cmd msg 13 16 14 17 15 18 port pause : () -> Cmd msg
+1 -1
src/Applications/UI/Tracks.elm
··· 465 465 ) 466 466 , ( Icon Icons.equalizer 467 467 , Label "Equalizer" Hidden 468 - , PerformMsg Bypass 468 + , UI.Navigation.GoToPage UI.Page.Equalizer 469 469 ) 470 470 ] 471 471 ]
+41 -10
src/Applications/UI/UserData.elm
··· 16 16 import Tracks.Encoding as Tracks 17 17 import UI.Backdrop 18 18 import UI.Core 19 + import UI.Equalizer as Equalizer 19 20 import UI.Notifications 20 21 import UI.Reply as UI 21 22 import UI.Sources as Sources ··· 121 122 122 123 exportEnclosed : UI.Core.Model -> Json.Value 123 124 exportEnclosed model = 125 + let 126 + equalizerSettings = 127 + { low = model.equalizer.low 128 + , mid = model.equalizer.mid 129 + , high = model.equalizer.high 130 + , volume = model.equalizer.volume 131 + } 132 + in 124 133 encodeEnclosed 125 134 { backgroundImage = model.backdrop.chosen 135 + , equalizerSettings = equalizerSettings 126 136 , onlyShowFavourites = model.tracks.favouritesOnly 127 137 , repeat = model.queue.repeat 128 138 , searchTerm = model.tracks.searchTerm ··· 135 145 importEnclosed : Json.Value -> UI.Core.Model -> R3D3 UI.Core.Model UI.Core.Msg UI.Reply 136 146 importEnclosed value model = 137 147 let 138 - { backdrop, queue, tracks } = 148 + { backdrop, equalizer, queue, tracks } = 139 149 model 140 150 in 141 151 case decodeEnclosed value of 142 152 Ok data -> 143 153 let 144 154 newBackDrop = 145 - { backdrop | chosen = Just (Maybe.withDefault UI.Backdrop.default data.backgroundImage) } 155 + { backdrop 156 + | chosen = Just (Maybe.withDefault UI.Backdrop.default data.backgroundImage) 157 + } 158 + 159 + newEqualizer = 160 + { equalizer 161 + | low = data.equalizerSettings.low 162 + , mid = data.equalizerSettings.mid 163 + , high = data.equalizerSettings.high 164 + , volume = data.equalizerSettings.volume 165 + } 146 166 147 167 newQueue = 148 - { queue | repeat = data.repeat, shuffle = data.shuffle } 168 + { queue 169 + | repeat = data.repeat 170 + , shuffle = data.shuffle 171 + } 149 172 150 173 newTracks = 151 - { tracks | favouritesOnly = data.onlyShowFavourites, searchTerm = data.searchTerm, sortBy = data.sortBy, sortDirection = data.sortDirection } 174 + { tracks 175 + | favouritesOnly = data.onlyShowFavourites 176 + , searchTerm = data.searchTerm 177 + , sortBy = data.sortBy 178 + , sortDirection = data.sortDirection 179 + } 152 180 in 153 - R3.withNothing 154 - { model 155 - | backdrop = newBackDrop 156 - , queue = newQueue 157 - , tracks = newTracks 158 - } 181 + ( { model 182 + | backdrop = newBackDrop 183 + , equalizer = newEqualizer 184 + , queue = newQueue 185 + , tracks = newTracks 186 + } 187 + , Cmd.map UI.Core.EqualizerMsg (Equalizer.adjustAllKnobs newEqualizer) 188 + , Nothing 189 + ) 159 190 160 191 Err err -> 161 192 err
+1 -1
src/Javascript/audio-engine.js
··· 80 80 81 81 function determineNodeGainValue(knobType, value) { 82 82 switch (knobType) { 83 - case "Volume" : return value 83 + case "VOLUME" : return value 84 84 default : return value < 0 ? value * 50 : value * 15 85 85 } 86 86 }
+17
src/Javascript/index.js
··· 61 61 }) 62 62 63 63 64 + app.ports.adjustEqualizerSetting.subscribe(e => { 65 + let node 66 + 67 + switch (e.knob) { 68 + case "LOW" : node = low; break 69 + case "MID" : node = mid; break 70 + case "HIGH" : node = high; break 71 + case "VOLUME" : node = volume; break 72 + } 73 + 74 + node.gain.setValueAtTime( 75 + determineNodeGainValue(e.knob, e.value), 76 + context.currentTime 77 + ) 78 + }) 79 + 80 + 64 81 app.ports.play.subscribe(_ => { 65 82 if (orchestrion.audio) orchestrion.audio.play() 66 83 })
+5 -1
src/Library/Authentication.elm
··· 1 1 module Authentication exposing (EnclosedUserData, HypaethralUserData, Method(..), decodeEnclosed, decodeHypaethral, decodeMethod, emptyHypaethralUserData, enclosedDecoder, encodeEnclosed, encodeHypaethral, encodeMethod, hypaethralDecoder, methodFromString, methodToString) 2 2 3 + import Equalizer 3 4 import Json.Decode as Json 4 5 import Json.Decode.Ext as Json 5 6 import Json.Decode.Pipeline exposing (optional, required) ··· 22 23 23 24 type alias EnclosedUserData = 24 25 { backgroundImage : Maybe String 26 + , equalizerSettings : Equalizer.Settings 25 27 , onlyShowFavourites : Bool 26 28 , repeat : Bool 27 29 , searchTerm : Maybe String ··· 88 90 enclosedDecoder = 89 91 Json.succeed EnclosedUserData 90 92 |> required "backgroundImage" (Json.maybe Json.string) 93 + |> optional "equalizerSettings" Equalizer.settingsDecoder Equalizer.defaultSettings 91 94 |> optional "onlyShowFavourites" Json.bool False 92 95 |> optional "repeat" Json.bool False 93 96 |> required "searchTerm" (Json.maybe Json.string) ··· 97 100 98 101 99 102 encodeEnclosed : EnclosedUserData -> Json.Value 100 - encodeEnclosed { backgroundImage, onlyShowFavourites, repeat, searchTerm, shuffle, sortBy, sortDirection } = 103 + encodeEnclosed { backgroundImage, equalizerSettings, onlyShowFavourites, repeat, searchTerm, shuffle, sortBy, sortDirection } = 101 104 Json.Encode.object 102 105 [ ( "backgroundImage", Maybe.unwrap Json.Encode.null Json.Encode.string backgroundImage ) 106 + , ( "equalizerSettings", Equalizer.encodeSettings equalizerSettings ) 103 107 , ( "onlyShowFavourites", Json.Encode.bool onlyShowFavourites ) 104 108 , ( "repeat", Json.Encode.bool repeat ) 105 109 , ( "searchTerm", Maybe.unwrap Json.Encode.null Json.Encode.string searchTerm )
+15 -3
src/Library/Color/Ext.elm
··· 1 - module Color.Ext exposing (toElmCssColor) 1 + module Color.Ext exposing (setOpacity, toElmCssColor) 2 2 3 - import Color 3 + import Color exposing (Color) 4 4 import Css 5 + 5 6 6 7 7 - toElmCssColor : Color.Color -> Css.Color 8 + -- 🔱 9 + 10 + 11 + setOpacity : Float -> Color -> Color 12 + setOpacity opacity color = 13 + color 14 + |> Color.toRgba 15 + |> (\c -> { c | alpha = opacity }) 16 + |> Color.fromRgba 17 + 18 + 19 + toElmCssColor : Color -> Css.Color 8 20 toElmCssColor color = 9 21 color 10 22 |> Color.toRgba
+49
src/Library/Equalizer.elm
··· 1 + module Equalizer exposing (Settings, defaultSettings, encodeSettings, settingsDecoder) 2 + 3 + import Json.Decode 4 + import Json.Encode 5 + 6 + 7 + 8 + -- 🌳 9 + 10 + 11 + type alias Settings = 12 + { low : Float 13 + , mid : Float 14 + , high : Float 15 + , volume : Float 16 + } 17 + 18 + 19 + 20 + -- 🔱 21 + 22 + 23 + defaultSettings : Settings 24 + defaultSettings = 25 + { low = 0 26 + , mid = 0 27 + , high = 0 28 + , volume = 0.5 29 + } 30 + 31 + 32 + encodeSettings : Settings -> Json.Encode.Value 33 + encodeSettings settings = 34 + Json.Encode.object 35 + [ ( "low", Json.Encode.float settings.low ) 36 + , ( "mid", Json.Encode.float settings.mid ) 37 + , ( "high", Json.Encode.float settings.high ) 38 + , ( "volume", Json.Encode.float settings.volume ) 39 + ] 40 + 41 + 42 + settingsDecoder : Json.Decode.Decoder Settings 43 + settingsDecoder = 44 + Json.Decode.map4 45 + Settings 46 + (Json.Decode.field "low" Json.Decode.float) 47 + (Json.Decode.field "mid" Json.Decode.float) 48 + (Json.Decode.field "high" Json.Decode.float) 49 + (Json.Decode.field "volume" Json.Decode.float)
+2 -2
src/Library/Replying.elm
··· 79 79 {-| Convenience function for returning the standard ( model, Cmd msg ) tuple. 80 80 -} 81 81 return : model -> Cmd msg -> ( model, Cmd msg ) 82 - return model msg = 83 - ( model, msg ) 82 + return = 83 + Tuple.pair 84 84 85 85 86 86 {-| Handle the state of a child.