From f1c07d86d2a6c4ed83add091facaebebf28f763d Mon Sep 17 00:00:00 2001 From: cbossut Date: Tue, 17 Nov 2020 16:29:50 +0100 Subject: [PATCH 01/43] fix node chdir in package --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 4447d92..d8a8f28 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,7 @@ Copyright ou © ou Copr. Clément Bossut, (2018) */ -process.chdir(__dirname) +if (!__dirname.includes("snapshot")) process.chdir(__dirname) console.log('args : port soundDir saveDir backUpDir') From 5ef18485ccd9a64d404d2708d0fdae14582b9501 Mon Sep 17 00:00:00 2001 From: cbossut Date: Fri, 18 Dec 2020 16:01:37 +0100 Subject: [PATCH 02/43] minor comments 'n' debugs --- ports.js | 1 + src/Data/Common.elm | 2 +- src/Data/Content.elm | 4 ++-- src/Harmony.elm | 2 ++ src/Main.elm | 4 ++++ 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ports.js b/ports.js index 0cde196..bec75bd 100644 --- a/ports.js +++ b/ports.js @@ -98,6 +98,7 @@ function cutSample(infos) { if (!buffers[infos.fromFileName]) {console.error(infos.fromFileName + " ain’t loaded, cannot cut");return;} let buf = buffers[infos.fromFileName] + // TODO maybe round ? , start = infos.percents[0] * buf.length - 1 , end = infos.percents[1] * buf.length + 1 , newBuf = new AudioBuffer( diff --git a/src/Data/Common.elm b/src/Data/Common.elm index 41773ae..a52ff4f 100644 --- a/src/Data/Common.elm +++ b/src/Data/Common.elm @@ -174,7 +174,7 @@ updateWheel ( id, list ) msg m = _ -> let _ = - Debug.log "Wrong identifier to delete bead" ( ( id, l ), msg, m ) + Debug.log "Wrong identifier to update bead" ( ( id, l ), msg, m ) in w in diff --git a/src/Data/Content.elm b/src/Data/Content.elm index 8fbaed0..2791b34 100644 --- a/src/Data/Content.elm +++ b/src/Data/Content.elm @@ -100,8 +100,8 @@ beadDecoder wheelDecoder = type alias Collar item = - { matrice : Int - , loop : Float + { matrice : Int -- nth bead to include in collar size (start to nth’s end) + , loop : Float -- start point of loop in percent of full collar (then loops to end) , head : Bead item , beads : List (Bead item) diff --git a/src/Harmony.elm b/src/Harmony.elm index 5cee6be..d066eb6 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -12,6 +12,8 @@ import Round -- TODO Keep Ref internal -- TODO Debug.log reveals impossible cases, maybe make fns with ’em intern and expose a safe version, higher level +-- TODO Why Other can’t point to Other ? every fn could be recursive this way and no fallback cases +-- But there could be circular references instead… type alias Harmony = diff --git a/src/Main.elm b/src/Main.elm index 43ba792..2ab3203 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -325,6 +325,10 @@ update msg model = Just str -> case searchReplacement str soundLib of Nothing -> + let + _ = + Debug.log "Cannot find replacement for a sound" str + in Nothing Just p -> From ff6003e61a6e8077fdaa6747e1cad31ded7a72d4 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 19 Dec 2020 17:45:58 +0100 Subject: [PATCH 03/43] Refactor so that Harmony can be Self ContentLength --- src/Data/Common.elm | 7 +- src/Data/Gear.elm | 43 +--------- src/Data/Mobile.elm | 48 ++++++++++- src/Editor/Mobile.elm | 60 +++++++------- src/Engine.elm | 7 +- src/Harmony.elm | 180 ++++++++++++++++++++++++++++++++---------- 6 files changed, 226 insertions(+), 119 deletions(-) diff --git a/src/Data/Common.elm b/src/Data/Common.elm index a52ff4f..7c7b6b4 100644 --- a/src/Data/Common.elm +++ b/src/Data/Common.elm @@ -181,6 +181,11 @@ updateWheel ( id, list ) msg m = Content.updateGear id (\gear -> { gear | wheel = rec list gear.wheel }) m +getWheeledContentLength : Wheeled g -> Float +getWheeledContentLength = + getContentLength << Wheel.getContent + + getContentLength : Conteet -> Float getContentLength c = case c of @@ -188,7 +193,7 @@ getContentLength c = Sound.length s Content.M m -> - Harmo.getLengthId m.motor m.gears + Harmo.getLengthId getWheeledContentLength m.motor m.gears Content.C col -> Content.getMatriceLength col diff --git a/src/Data/Gear.elm b/src/Data/Gear.elm index f38641d..087e34a 100644 --- a/src/Data/Gear.elm +++ b/src/Data/Gear.elm @@ -1,12 +1,11 @@ module Data.Gear exposing (..) -import Coll exposing (Coll, Id) +import Coll exposing (Id) import Fraction as Fract exposing (Fraction) import Harmony as Harmo exposing (Harmony) import Json.Decode as D import Json.Decode.Field as Field import Json.Encode as E -import Link exposing (DrawLink, Link) import Math.Vector2 as Vec exposing (Vec2, vec2) import Motor exposing (Motor) @@ -30,46 +29,6 @@ toUID id = typeString ++ "-" ++ Coll.idToString id -copy : Id (Gear w) -> Coll (Gear w) -> Coll (Gear w) -copy id coll = - let - g = - Coll.get id coll - - base = - Coll.idMap <| Maybe.withDefault id <| Harmo.getBaseId g.harmony - - newG = - { g - | pos = Vec.add g.pos (vec2 (Harmo.getLength g.harmony coll * 1.1) 0) - , harmony = { fract = g.harmony.fract, ref = Harmo.Other <| Coll.idMap base } -- TODO abuses harmo internals - , motor = [] - } - - ( newId, newColl ) = - Coll.insertTellId newG coll - - newLink = - Link.map ( id, newId ) - in - Coll.update base (Harmo.insert newId >> Harmo.addLink newLink) newColl - - -toDrawLink : Coll (Gear w) -> Link (Gear w) -> DrawLink -toDrawLink coll l = - let - get id = - Coll.get id coll - - toCircle g = - { c = g.pos, d = Harmo.getLength g.harmony coll } - - f = - get >> toCircle - in - Tuple.mapBoth f f l - - encoder : (w -> List ( String, E.Value )) -> Gear w -> E.Value encoder wEncoder g = E.object <| diff --git a/src/Data/Mobile.elm b/src/Data/Mobile.elm index 4eca0cb..20722e6 100644 --- a/src/Data/Mobile.elm +++ b/src/Data/Mobile.elm @@ -8,7 +8,8 @@ import Data.Wheel as Wheel exposing (Conteet, Wheel) import Harmony as Harmo exposing (Harmony) import Json.Decode as D import Json.Encode as E -import Math.Vector2 exposing (Vec2) +import Link exposing (DrawLink, Link) +import Math.Vector2 as Vec exposing (Vec2) import Motor @@ -59,6 +60,39 @@ newSizedGear p l w = { pos = p, harmony = Harmo.newSelf l, motor = [], wheel = w } +copy : Vec2 -> Id Geer -> Coll Geer -> Coll Geer +copy move id coll = + let + g = + Coll.get id coll + + newG = + { g + | pos = Vec.add g.pos move + , motor = [] + } + + ( newId, newColl ) = + Coll.insertTellId newG coll + in + Harmo.makeCopy id newId newColl + + +toDrawLink : Coll Geer -> Link Geer -> DrawLink +toDrawLink coll l = + let + get id = + Coll.get id coll + + toCircle g = + { c = g.pos, d = getLength g coll } + + f = + get >> toCircle + in + Tuple.mapBoth f f l + + -- TODO remove and use Common.getName instead @@ -76,13 +110,23 @@ gearName id coll = name +getLengthId : Id Geer -> Coll Geer -> Float +getLengthId = + Harmo.getLengthId getWheeledContentLength + + +getLength : Geer -> Coll Geer -> Float +getLength = + Harmo.getLength getWheeledContentLength + + gearPosSize : Id Geer -> Coll Geer -> ( Vec2, Float ) gearPosSize id coll = let g = Coll.get id coll in - ( g.pos, Harmo.getLength g.harmony coll ) + ( g.pos, getLength g coll ) rm : Id Geer -> Mobeel -> Mobeel diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 99ef2c0..9bc03da 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -442,7 +442,11 @@ update msg ( model, mobile ) = return -} CopyGear id -> - { return | mobile = { mobile | gears = Gear.copy id mobile.gears }, toUndo = Do } + let + d = + vec2 (Mobile.getLengthId id mobile.gears * 1.1) 0 + in + { return | mobile = { mobile | gears = Mobile.copy d id mobile.gears }, toUndo = Do } CopyContent w -> case model.edit of @@ -502,7 +506,7 @@ update msg ( model, mobile ) = Maybe.map Collar.getTotalLength mayNewCol oldLength = - Harmo.getLengthId id mobile.gears + Mobile.getLengthId id mobile.gears in case Maybe.map2 Tuple.pair mayOldContentLength mayNewContentLength of Just ( oldCL, newCL ) -> @@ -735,7 +739,7 @@ update msg ( model, mobile ) = { mobile | gears = Harmo.changeSelf id - (CommonData.getContentLength <| Wheel.getContent <| Coll.get id mobile.gears) + (CommonData.getWheeledContentLength <| Coll.get id mobile.gears) mobile.gears } , toUndo = Do @@ -760,7 +764,7 @@ update msg ( model, mobile ) = Coll.get id mobile.gears newMotor = - { m | motor = Motor.default, harmony = Harmo.newSelf <| Harmo.getLength m.harmony mobile.gears } + { m | motor = Motor.default, harmony = Harmo.newSelf <| Mobile.getLength m mobile.gears } subMobile = List.foldl @@ -772,7 +776,7 @@ update msg ( model, mobile ) = newG = { g | motor = Motor.default - , harmony = Harmo.newSelf <| Harmo.getLength g.harmony mobile.gears + , harmony = Harmo.newSelf <| Mobile.getLength g mobile.gears } in { acc | gears = Coll.insert newG acc.gears } @@ -797,7 +801,7 @@ update msg ( model, mobile ) = Coll.get id mobile.gears l = - Harmo.getLength g.harmony mobile.gears + Mobile.getLength g mobile.gears collar = case collaring of @@ -1298,7 +1302,7 @@ viewContent ( model, mobile ) = in Wheel.view w g.pos - (Harmo.getLength g.harmony mobile.gears) + (Mobile.getLength g mobile.gears) { mod = getMod id , motor = id == mobile.motor , dashed = Harmo.hasHarmonics g.harmony @@ -1330,14 +1334,14 @@ viewContent ( model, mobile ) = Play _ _ -> let length = - Harmo.getLength g.harmony mobile.gears + Mobile.getLength g mobile.gears in [ Link.drawMotorLink ( ( g.pos, length ), ( pos, length ) ) ] Harmonize -> [ Link.drawRawLink ( g.pos, pos ) - (Harmo.getLength g.harmony mobile.gears) + (Mobile.getLength g mobile.gears) Link.baseColor ] ++ [ S.g [ SA.opacity <| Opacity 0 ] <| @@ -1345,7 +1349,7 @@ viewContent ( model, mobile ) = (\( idd, gg ) -> Wheel.view gg.wheel gg.pos - (Harmo.getLength gg.harmony mobile.gears) + (Mobile.getLength gg mobile.gears) Wheel.defaultStyle (Just ( IWheel << Tuple.pair idd, [] )) Nothing @@ -1361,10 +1365,10 @@ viewContent ( model, mobile ) = CompleteLink l -> case model.tool of Play _ _ -> - Link.viewMotorLink False <| Gear.toDrawLink mobile.gears l + Link.viewMotorLink False <| Mobile.toDrawLink mobile.gears l Harmonize -> - Link.viewFractLink (Gear.toDrawLink mobile.gears l) <| ILink l + Link.viewFractLink (Mobile.toDrawLink mobile.gears l) <| ILink l _ -> [] @@ -1409,7 +1413,7 @@ viewContent ( model, mobile ) = List.concatMap (\l -> Link.viewMotorLink (List.any (Link.equal l) cuts) <| - Gear.toDrawLink mobile.gears l + Mobile.toDrawLink mobile.gears l ) <| Motor.getAllLinks mobile.gears @@ -1417,7 +1421,7 @@ viewContent ( model, mobile ) = Harmonize -> (case Interact.getInteract model.interact of Just ( ILink l, _ ) -> - Link.viewFractOnLink (Gear.toDrawLink mobile.gears l) <| + Link.viewFractOnLink (Mobile.toDrawLink mobile.gears l) <| Fract.simplify <| Fract.division (Coll.get (Tuple.second l) mobile.gears).harmony.fract @@ -1426,13 +1430,13 @@ viewContent ( model, mobile ) = _ -> [] ) - ++ (List.concatMap (\l -> Link.viewFractLink (Gear.toDrawLink mobile.gears l) (ILink l)) <| + ++ (List.concatMap (\l -> Link.viewFractLink (Mobile.toDrawLink mobile.gears l) (ILink l)) <| List.concatMap (.harmony >> Harmo.getLinks) <| Coll.values mobile.gears ) ++ (case model.link of Just { link, fractInput } -> - Link.viewSelectedLink (Gear.toDrawLink mobile.gears link) <| + Link.viewSelectedLink (Mobile.toDrawLink mobile.gears link) <| case fractInput of FractionInput _ _ _ -> Just <| @@ -1456,7 +1460,7 @@ viewContent ( model, mobile ) = Coll.get id mobile.gears length = - Harmo.getLength g.harmony mobile.gears + Mobile.getLength g mobile.gears pos = g.pos @@ -1803,10 +1807,10 @@ viewEditDetails model mobile = (\rId -> getNameWithDefault rId mobile ) - , text <| "( " ++ (Round.round 2 <| Harmo.getLengthId id mobile.gears) ++ " )" + , text <| "( " ++ (Round.round 2 <| Mobile.getLengthId id mobile.gears) ++ " )" , text <| "Contenu : " - ++ (Round.round 2 <| CommonData.getContentLength <| Wheel.getContent g) + ++ (Round.round 2 <| CommonData.getWheeledContentLength g) ] ++ (case Wheel.getContent g of Content.S s -> @@ -1923,8 +1927,8 @@ viewHarmonizeDetails model mobile = Round.round 5 <| - Harmo.getLengthId (Tuple.second link) mobile.gears - / Harmo.getLengthId (Tuple.first link) mobile.gears + Mobile.getLengthId (Tuple.second link) mobile.gears + / Mobile.getLengthId (Tuple.first link) mobile.gears , text = str , label = Input.labelHidden "Fraction" , onChange = EnteredTextFract @@ -2002,7 +2006,7 @@ doResize id d add mobile = mobile.gears length = - Harmo.getLengthId id gears + Mobile.getLengthId id gears dd = if add then @@ -2083,7 +2087,7 @@ addBead model mobile bead = mobile oldLength = - Harmo.getLengthId id mobile.gears + Mobile.getLengthId id mobile.gears oldContentLength = Collar.getTotalLength col @@ -2107,7 +2111,7 @@ addBead model mobile bead = computeCuts : ( Vec2, Vec2 ) -> Coll Geer -> List (Link Geer) computeCuts cut gears = Motor.getAllLinks gears - |> List.filter (Link.cuts cut << Link.toSegment << Gear.toDrawLink gears) + |> List.filter (Link.cuts cut << Link.toSegment << Mobile.toDrawLink gears) @@ -2402,7 +2406,7 @@ manageInteractEvent event model mobile = / (Tuple.second oldPercents - Tuple.first oldPercents) oldLength = - Harmo.getLengthId id mobile.gears + Mobile.getLengthId id mobile.gears in { ret | mobile = { newMob | gears = Harmo.changeSelf id (ratio * oldLength) newMob.gears } } @@ -2539,7 +2543,7 @@ interactHarmonize event model mobile = case ( event.item, event.action, model.dragging ) of -- COPY ( IWheel ( id, [] ), Interact.Clicked _, _ ) -> - { return | mobile = { mobile | gears = Gear.copy id mobile.gears }, toUndo = Do } + update (CopyGear id) ( model, mobile ) -- RESIZE ( IResizeHandle id add, Interact.Dragged { startD } _ _, NoDrag ) -> @@ -2699,7 +2703,7 @@ interactMove event model mobile = (Pack.DragTo <| Just { pos = newPos - , length = Harmo.getLengthId id mobile.gears + , length = Mobile.getLengthId id mobile.gears , wheel = (Coll.get id mobile.gears).wheel } ) @@ -2736,7 +2740,7 @@ interactMove event model mobile = Wheel.getLoopPercents waveG selPercentLength = - Harmo.getLengthId id mobile.gears * (end - start) / Harmo.getLength waveG.harmony mobile.gears + Mobile.getLengthId id mobile.gears * (end - start) / Mobile.getLength waveG mobile.gears ( wave, cmd ) = Waveform.update (Waveform.Select ( Vec.getX oldPos, selPercentLength )) model.wave diff --git a/src/Engine.elm b/src/Engine.elm index d91397a..7b61ebb 100644 --- a/src/Engine.elm +++ b/src/Engine.elm @@ -15,9 +15,8 @@ import Data.Collar as Collar exposing (Beed, Colleer) import Data.Common exposing (Identifier) import Data.Content as Content import Data.Gear as Gear exposing (Gear) -import Data.Mobile exposing (Geer, Mobeel) +import Data.Mobile as Mobile exposing (Geer, Mobeel) import Data.Wheel as Wheel exposing (Wheel) -import Harmony as Harmo import Json.Encode as E import Motor import Sound @@ -129,7 +128,7 @@ encodeGear hasView parentUid coll id = Coll.get id coll length = - Harmo.getLength g.harmony coll + Mobile.getLength g coll uid = parentUid ++ Gear.toUID id @@ -153,7 +152,7 @@ encodeGear hasView parentUid coll id = encodeMobile : Mobeel -> Bool -> String -> E.Value encodeMobile { motor, gears } hasView parentUid = E.object - [ ( "duration", E.float <| Harmo.getLengthId motor gears ) + [ ( "duration", E.float <| Mobile.getLengthId motor gears ) , ( "gears", E.list (encodeGear hasView parentUid gears) <| Motor.getMotored motor gears ) ] diff --git a/src/Harmony.elm b/src/Harmony.elm index d066eb6..4d678e9 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -43,10 +43,15 @@ setFract fract g = { g | harmony = { harmo | fract = fract } } +type SelfUnit + = ContentLength + | Unit Float + + type Ref = Other (Id Harmony) | Self - { unit : Float + { unit : SelfUnit -- TODO better be a Set than a List, either deOpacify Id or add Set in Coll lib , group : List (Id Harmony) @@ -61,15 +66,28 @@ view id coll getName = getHarmo id coll in Fract.toString harmo.fract - ++ " de " ++ (case harmo.ref of - Self r -> - Round.round 2 r.unit + Self { unit } -> + case unit of + Unit float -> + " de " ++ Round.round 2 float + + ContentLength -> + " du contenu" Other rId -> + let + name = + getName <| Coll.idMap rId + in case (Coll.get (Coll.idMap rId) coll).harmony.ref of - Self r -> - Round.round 2 r.unit ++ " ( " ++ (getName <| Coll.idMap rId) ++ " )" + Self { unit } -> + case unit of + Unit float -> + " de " ++ Round.round 2 float ++ " ( " ++ name ++ " )" + + ContentLength -> + " du contenu de " ++ name Other _ -> Debug.log "IMPOSSIBLE Other refer to another Other" "BUG Harmo.view" @@ -78,7 +96,7 @@ view id coll getName = defaultRef : Ref defaultRef = - Self { unit = 0, group = [], links = [] } + Self { unit = ContentLength, group = [], links = [] } clean : Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g) @@ -102,7 +120,7 @@ changeSelf id length coll = in case harmo.ref of Self r -> - Coll.update id (always { g | harmony = { harmo | ref = Self { r | unit = length } } }) coll + Coll.update id (always { g | harmony = { harmo | ref = Self { r | unit = Unit length } } }) coll Other rId -> coll @@ -115,37 +133,94 @@ resizeFree id length coll = changeSelf id (length / Fract.toFloat (getHarmo id coll).fract) coll -getLengthId : Id (Harmonized g) -> Coll (Harmonized g) -> Float -getLengthId id coll = - getLength (getHarmo id coll) coll +toContentLength : Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g) +toContentLength id coll = + case (getHarmo id coll).ref of + Self _ -> + Coll.update id (\g -> { g | harmony = newContentLength }) coll + + Other rId -> + coll + |> Coll.update id (\g -> { g | harmony = newContentLength }) + |> Coll.update (Coll.idMap rId) (remove id) + +makeCopy : Id (Harmonized g) -> Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g) +makeCopy id newId coll = + let + harmo = + getHarmo id coll -getLength : Harmony -> Coll (Harmonized g) -> Float -getLength harmo coll = - case harmo.ref of - Self { unit } -> - unit * Fract.toFloat harmo.fract + baseId = + Maybe.withDefault id <| getBaseId harmo - Other id -> - let - { ref } = - (Coll.get (Coll.idMap id) coll).harmony - in - case ref of + newHarmo = + { fract = harmo.fract, ref = Other <| Coll.idMap baseId } + + link = + ( id, newId ) + in + coll + |> Coll.update newId (\g -> { g | harmony = newHarmo }) + |> Coll.update id (insert newId >> addLink link) + + +getLengthId : (Harmonized g -> Float) -> Id (Harmonized g) -> Coll (Harmonized g) -> Float +getLengthId f id coll = + getLength f (Coll.get id coll) coll + + +getLength : (Harmonized g -> Float) -> Harmonized g -> Coll (Harmonized g) -> Float +getLength getContentLength el coll = + let + harmo = + el.harmony + + refUnit = + case harmo.ref of Self { unit } -> - unit * Fract.toFloat harmo.fract + case unit of + Unit float -> + float + + ContentLength -> + getContentLength el - Other _ -> + Other idd -> let - _ = - Debug.log "IMPOSSIBLE Ref isn’t a base" ( harmo, coll ) + ell = + Coll.get (Coll.idMap idd) coll + + { ref } = + ell.harmony in - 0 + case ref of + Self { unit } -> + case unit of + Unit float -> + float + + ContentLength -> + getContentLength ell + + Other _ -> + let + _ = + Debug.log "IMPOSSIBLE Ref isn’t a base" ( harmo, coll ) + in + getContentLength el + in + Fract.toFloat harmo.fract * refUnit newSelf : Float -> Harmony newSelf length = - { fract = Fract.integer 1, ref = Self { unit = length, group = [], links = [] } } + { fract = Fract.integer 1, ref = Self { unit = Unit length, group = [], links = [] } } + + +newContentLength : Harmony +newContentLength = + { fract = Fract.integer 1, ref = Self { unit = ContentLength, group = [], links = [] } } hasHarmonics : Harmony -> Bool @@ -286,6 +361,25 @@ decoder = } +selfUnitEncoder : SelfUnit -> List ( String, E.Value ) +selfUnitEncoder su = + case su of + ContentLength -> + [] + + Unit f -> + [ ( "unit", E.float f ) ] + + +selfUnitDecoder : D.Decoder SelfUnit +selfUnitDecoder = + Field.attempt "unit" D.float <| + \mayUnit -> + D.succeed <| + Maybe.withDefault ContentLength <| + Maybe.map Unit mayUnit + + refEncoder : Ref -> E.Value refEncoder ref = case ref of @@ -294,10 +388,10 @@ refEncoder ref = Self r -> E.object <| - [ ( "unit", E.float r.unit ) - , ( "group", E.list Coll.idEncoder r.group ) + [ ( "group", E.list Coll.idEncoder r.group ) , ( "links", E.list Link.encoder r.links ) ] + ++ selfUnitEncoder r.unit refDecoder : D.Decoder Ref @@ -309,15 +403,17 @@ refDecoder = D.succeed <| Other id Nothing -> - Field.require "unit" D.float <| - \unit -> - Field.require "group" (D.list Coll.idDecoder) <| - \group -> - Field.require "links" (D.list Link.decoder) <| - \links -> - D.succeed <| - Self - { unit = unit - , group = group - , links = links - } + selfUnitDecoder + |> (D.andThen <| + \unit -> + Field.require "group" (D.list Coll.idDecoder) <| + \group -> + Field.require "links" (D.list Link.decoder) <| + \links -> + D.succeed <| + Self + { unit = unit + , group = group + , links = links + } + ) From e0caf548c342b9156fe946119eac81dbf6643eab Mon Sep 17 00:00:00 2001 From: cbossut Date: Tue, 22 Dec 2020 15:37:35 +0100 Subject: [PATCH 04/43] Rerefactor Harmony Self ContentLength to Rate And renamed accordingly --- src/Data/Mobile.elm | 4 +-- src/Editor/Mobile.elm | 4 +-- src/Harmony.elm | 78 +++++++++++++++++++++---------------------- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/Data/Mobile.elm b/src/Data/Mobile.elm index 20722e6..9674d0c 100644 --- a/src/Data/Mobile.elm +++ b/src/Data/Mobile.elm @@ -49,7 +49,7 @@ defaultGear = gearFromContent : Conteet -> Vec2 -> Geer gearFromContent c pos = { pos = pos - , harmony = Harmo.newSelf <| getContentLength c + , harmony = Harmo.newDuration <| getContentLength c , motor = [] , wheel = Wheel.fromContent c } @@ -57,7 +57,7 @@ gearFromContent c pos = newSizedGear : Vec2 -> Float -> Wheel -> Geer newSizedGear p l w = - { pos = p, harmony = Harmo.newSelf l, motor = [], wheel = w } + { pos = p, harmony = Harmo.newDuration l, motor = [], wheel = w } copy : Vec2 -> Id Geer -> Coll Geer -> Coll Geer diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 9bc03da..07fd20e 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -764,7 +764,7 @@ update msg ( model, mobile ) = Coll.get id mobile.gears newMotor = - { m | motor = Motor.default, harmony = Harmo.newSelf <| Mobile.getLength m mobile.gears } + { m | motor = Motor.default, harmony = Harmo.newDuration <| Mobile.getLength m mobile.gears } subMobile = List.foldl @@ -776,7 +776,7 @@ update msg ( model, mobile ) = newG = { g | motor = Motor.default - , harmony = Harmo.newSelf <| Mobile.getLength g mobile.gears + , harmony = Harmo.newDuration <| Mobile.getLength g mobile.gears } in { acc | gears = Coll.insert newG acc.gears } diff --git a/src/Harmony.elm b/src/Harmony.elm index 4d678e9..fad2820 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -44,8 +44,8 @@ setFract fract g = type SelfUnit - = ContentLength - | Unit Float + = Rate Float -- Duration / Content length + | Duration Float type Ref @@ -69,11 +69,11 @@ view id coll getName = ++ (case harmo.ref of Self { unit } -> case unit of - Unit float -> - " de " ++ Round.round 2 float + Duration d -> + " de " ++ Round.round 2 d - ContentLength -> - " du contenu" + Rate r -> + " du contenu x" ++ Round.round 2 (1 / r) Other rId -> let @@ -83,11 +83,11 @@ view id coll getName = case (Coll.get (Coll.idMap rId) coll).harmony.ref of Self { unit } -> case unit of - Unit float -> - " de " ++ Round.round 2 float ++ " ( " ++ name ++ " )" + Duration d -> + " de " ++ Round.round 2 d ++ " ( " ++ name ++ " )" - ContentLength -> - " du contenu de " ++ name + Rate r -> + " du contenu de " ++ name ++ " x" ++ Round.round 2 (1 / r) Other _ -> Debug.log "IMPOSSIBLE Other refer to another Other" "BUG Harmo.view" @@ -96,7 +96,7 @@ view id coll getName = defaultRef : Ref defaultRef = - Self { unit = ContentLength, group = [], links = [] } + Self { unit = Rate 1, group = [], links = [] } clean : Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g) @@ -110,7 +110,7 @@ clean id coll = changeSelf : Id (Harmonized g) -> Float -> Coll (Harmonized g) -> Coll (Harmonized g) -changeSelf id length coll = +changeSelf id dur coll = let g = Coll.get id coll @@ -120,11 +120,11 @@ changeSelf id length coll = in case harmo.ref of Self r -> - Coll.update id (always { g | harmony = { harmo | ref = Self { r | unit = Unit length } } }) coll + Coll.update id (always { g | harmony = { harmo | ref = Self { r | unit = Duration dur } } }) coll Other rId -> coll - |> Coll.update id (always { g | harmony = newSelf length }) + |> Coll.update id (always { g | harmony = newDuration dur }) |> Coll.update (Coll.idMap rId) (remove id) @@ -137,11 +137,11 @@ toContentLength : Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g toContentLength id coll = case (getHarmo id coll).ref of Self _ -> - Coll.update id (\g -> { g | harmony = newContentLength }) coll + Coll.update id (\g -> { g | harmony = newRate 1 }) coll Other rId -> coll - |> Coll.update id (\g -> { g | harmony = newContentLength }) + |> Coll.update id (\g -> { g | harmony = newRate 1 }) |> Coll.update (Coll.idMap rId) (remove id) @@ -180,11 +180,11 @@ getLength getContentLength el coll = case harmo.ref of Self { unit } -> case unit of - Unit float -> - float + Duration d -> + d - ContentLength -> - getContentLength el + Rate r -> + r * getContentLength el Other idd -> let @@ -197,11 +197,11 @@ getLength getContentLength el coll = case ref of Self { unit } -> case unit of - Unit float -> - float + Duration d -> + d - ContentLength -> - getContentLength ell + Rate r -> + r * getContentLength ell Other _ -> let @@ -213,14 +213,14 @@ getLength getContentLength el coll = Fract.toFloat harmo.fract * refUnit -newSelf : Float -> Harmony -newSelf length = - { fract = Fract.integer 1, ref = Self { unit = Unit length, group = [], links = [] } } +newDuration : Float -> Harmony +newDuration d = + { fract = Fract.integer 1, ref = Self { unit = Duration d, group = [], links = [] } } -newContentLength : Harmony -newContentLength = - { fract = Fract.integer 1, ref = Self { unit = ContentLength, group = [], links = [] } } +newRate : Float -> Harmony +newRate r = + { fract = Fract.integer 1, ref = Self { unit = Rate r, group = [], links = [] } } hasHarmonics : Harmony -> Bool @@ -364,20 +364,20 @@ decoder = selfUnitEncoder : SelfUnit -> List ( String, E.Value ) selfUnitEncoder su = case su of - ContentLength -> - [] + Rate r -> + [ ( "rate", E.float r ) ] - Unit f -> - [ ( "unit", E.float f ) ] + Duration d -> + [ ( "duration", E.float d ) ] selfUnitDecoder : D.Decoder SelfUnit selfUnitDecoder = - Field.attempt "unit" D.float <| - \mayUnit -> - D.succeed <| - Maybe.withDefault ContentLength <| - Maybe.map Unit mayUnit + D.oneOf + [ Field.require "unit" D.float <| \d -> D.succeed <| Duration d + , Field.require "duration" D.float <| \d -> D.succeed <| Duration d + , Field.require "rate" D.float <| \r -> D.succeed <| Rate r + ] refEncoder : Ref -> E.Value From 9e122f0f7eae51a3b13275e1707ee01d008bb9e7 Mon Sep 17 00:00:00 2001 From: cbossut Date: Tue, 22 Dec 2020 20:59:07 +0100 Subject: [PATCH 05/43] Decode old Harmo Self Unit to Rate when loading old file --- src/Data/Collar.elm | 2 +- src/Data/Content.elm | 12 ++++++------ src/Data/Gear.elm | 6 +++--- src/Data/Mobile.elm | 2 +- src/Data/Wheel.elm | 6 +++--- src/Harmony.elm | 18 +++++++++--------- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Data/Collar.elm b/src/Data/Collar.elm index dd4d60a..876e5ed 100644 --- a/src/Data/Collar.elm +++ b/src/Data/Collar.elm @@ -186,4 +186,4 @@ encoder = decoder : D.Decoder Colleer decoder = - Content.collarDecoder Wheel.decoder + Content.collarDecoder (Wheel.decoder getContentLength) diff --git a/src/Data/Content.elm b/src/Data/Content.elm index 2791b34..bc138dc 100644 --- a/src/Data/Content.elm +++ b/src/Data/Content.elm @@ -28,11 +28,11 @@ encoder wheelEncoder content = ( "collar", collarEncoder wheelEncoder c ) -decoder : D.Decoder item -> item -> D.Decoder (Content item) -decoder wheelDecoder defaultWheel = +decoder : D.Decoder item -> (item -> Float) -> item -> D.Decoder (Content item) +decoder wheelDecoder wheelToCententLength defaultWheel = D.oneOf [ Field.require "sound" Sound.decoder <| \sound -> D.succeed <| S sound - , Field.require "mobile" (mobileDecoder wheelDecoder defaultWheel) <| \mobile -> D.succeed <| M mobile + , Field.require "mobile" (mobileDecoder wheelDecoder wheelToCententLength defaultWheel) <| \mobile -> D.succeed <| M mobile , Field.require "collar" (collarDecoder wheelDecoder) <| \collar -> D.succeed <| C collar ] @@ -61,13 +61,13 @@ mobileEncoder wheelEncoder m = ] -mobileDecoder : D.Decoder item -> item -> D.Decoder (Mobile item) -mobileDecoder wheelDecoder defaultWheel = +mobileDecoder : D.Decoder item -> (item -> Float) -> item -> D.Decoder (Mobile item) +mobileDecoder wheelDecoder wheelToContentLength defaultWheel = D.succeed Mobile |> required "motor" Coll.idDecoder |> required "gears" (Coll.decoder - (Gear.decoder wheelDecoder) + (Gear.decoder wheelDecoder wheelToContentLength) Gear.typeString <| Gear.default defaultWheel diff --git a/src/Data/Gear.elm b/src/Data/Gear.elm index 087e34a..62dea71 100644 --- a/src/Data/Gear.elm +++ b/src/Data/Gear.elm @@ -44,12 +44,12 @@ encoder wEncoder g = ++ wEncoder g.wheel -decoder : D.Decoder w -> D.Decoder (Gear w) -decoder wDecoder = +decoder : D.Decoder w -> (w -> Float) -> D.Decoder (Gear w) +decoder wDecoder wheelToContentLength = wDecoder |> D.andThen (\w -> - Harmo.decoder + Harmo.decoder (wheelToContentLength w) |> D.andThen (\harmo -> Field.require "motors" Motor.decoder <| diff --git a/src/Data/Mobile.elm b/src/Data/Mobile.elm index 9674d0c..f7956fe 100644 --- a/src/Data/Mobile.elm +++ b/src/Data/Mobile.elm @@ -177,4 +177,4 @@ encoder = decoder : D.Decoder Mobeel decoder = - Content.mobileDecoder Wheel.decoder Wheel.default + Content.mobileDecoder (Wheel.decoder getContentLength) (getContentLength << Wheel.getWheelContent) Wheel.default diff --git a/src/Data/Wheel.elm b/src/Data/Wheel.elm index 1b7b907..db818e1 100644 --- a/src/Data/Wheel.elm +++ b/src/Data/Wheel.elm @@ -500,9 +500,9 @@ encoder w = ] -decoder : D.Decoder Wheel -decoder = - Content.decoder (D.lazy (\_ -> decoder)) default +decoder : (Conteet -> Float) -> D.Decoder Wheel +decoder getContentLength = + Content.decoder (D.lazy (\_ -> decoder getContentLength)) (getContentLength << getWheelContent) default |> D.andThen (\content -> Field.attempt "viewContent" D.bool <| diff --git a/src/Harmony.elm b/src/Harmony.elm index fad2820..136fb29 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -349,9 +349,9 @@ encoder h = ] -decoder : D.Decoder Harmony -decoder = - Field.require "ref" refDecoder <| +decoder : Float -> D.Decoder Harmony +decoder contentLength = + Field.require "ref" (refDecoder contentLength) <| \ref -> Field.require "fract" Fract.decoder <| \fract -> @@ -371,10 +371,10 @@ selfUnitEncoder su = [ ( "duration", E.float d ) ] -selfUnitDecoder : D.Decoder SelfUnit -selfUnitDecoder = +selfUnitDecoder : Float -> D.Decoder SelfUnit +selfUnitDecoder contentLength = D.oneOf - [ Field.require "unit" D.float <| \d -> D.succeed <| Duration d + [ Field.require "unit" D.float <| \d -> D.succeed <| Rate (d / contentLength) , Field.require "duration" D.float <| \d -> D.succeed <| Duration d , Field.require "rate" D.float <| \r -> D.succeed <| Rate r ] @@ -394,8 +394,8 @@ refEncoder ref = ++ selfUnitEncoder r.unit -refDecoder : D.Decoder Ref -refDecoder = +refDecoder : Float -> D.Decoder Ref +refDecoder contentLength = Field.attempt "other" Coll.idDecoder <| \mayId -> case mayId of @@ -403,7 +403,7 @@ refDecoder = D.succeed <| Other id Nothing -> - selfUnitDecoder + selfUnitDecoder contentLength |> (D.andThen <| \unit -> Field.require "group" (D.list Coll.idDecoder) <| From 0b85fca0fc29d92cde84d994a0aacf0c392ec68b Mon Sep 17 00:00:00 2001 From: cbossut Date: Tue, 22 Dec 2020 23:17:20 +0100 Subject: [PATCH 06/43] Use Rate instead of Duration everywhere by default Breaks Content Change, see TODO --- src/Data/Mobile.elm | 4 +- src/Editor/Mobile.elm | 107 ++++++++---------------------------------- src/Harmony.elm | 33 +++++++++---- 3 files changed, 46 insertions(+), 98 deletions(-) diff --git a/src/Data/Mobile.elm b/src/Data/Mobile.elm index f7956fe..080d0f8 100644 --- a/src/Data/Mobile.elm +++ b/src/Data/Mobile.elm @@ -49,7 +49,7 @@ defaultGear = gearFromContent : Conteet -> Vec2 -> Geer gearFromContent c pos = { pos = pos - , harmony = Harmo.newDuration <| getContentLength c + , harmony = Harmo.newRate 1 , motor = [] , wheel = Wheel.fromContent c } @@ -57,7 +57,7 @@ gearFromContent c pos = newSizedGear : Vec2 -> Float -> Wheel -> Geer newSizedGear p l w = - { pos = p, harmony = Harmo.newDuration l, motor = [], wheel = w } + { pos = p, harmony = Harmo.newRate (l / getWheeledContentLength { wheel = w }), motor = [], wheel = w } copy : Vec2 -> Id Geer -> Coll Geer -> Coll Geer diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 07fd20e..ca69ed2 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -472,52 +472,6 @@ update msg ( model, mobile ) = } DeleteWheel ( id, l ) -> - let - newMob = - CommonData.deleteWheel ( id, l ) mobile Mobile.rm Collar.rm - - finalMob = - case l of - [ i ] -> - let - colId = - ( id, [] ) - - mayOldCol = - case Wheel.getWheelContent <| CommonData.getWheel colId mobile of - Content.C col -> - Just col - - _ -> - Nothing - - mayNewCol = - case Wheel.getWheelContent <| CommonData.getWheel colId newMob of - Content.C col -> - Just col - - _ -> - Nothing - - mayOldContentLength = - Maybe.map Collar.getTotalLength mayOldCol - - mayNewContentLength = - Maybe.map Collar.getTotalLength mayNewCol - - oldLength = - Mobile.getLengthId id mobile.gears - in - case Maybe.map2 Tuple.pair mayOldContentLength mayNewContentLength of - Just ( oldCL, newCL ) -> - { newMob | gears = Harmo.changeSelf id (newCL * oldLength / oldCL) newMob.gears } - - Nothing -> - newMob - - _ -> - newMob - in { return | model = { model @@ -551,7 +505,7 @@ update msg ( model, mobile ) = } , toUndo = Do , toEngine = [ Engine.stop ] - , mobile = finalMob + , mobile = CommonData.deleteWheel ( id, l ) mobile Mobile.rm Collar.rm } EnteredFract isNumerator str -> @@ -738,9 +692,7 @@ update msg ( model, mobile ) = | mobile = { mobile | gears = - Harmo.changeSelf id - (CommonData.getWheeledContentLength <| Coll.get id mobile.gears) - mobile.gears + Harmo.toContentLength id mobile.gears } , toUndo = Do } @@ -764,7 +716,11 @@ update msg ( model, mobile ) = Coll.get id mobile.gears newMotor = - { m | motor = Motor.default, harmony = Harmo.newDuration <| Mobile.getLength m mobile.gears } + { m + | motor = Motor.default + , harmony = + Harmo.newRate (Mobile.getLength m mobile.gears / CommonData.getWheeledContentLength m) + } subMobile = List.foldl @@ -776,7 +732,9 @@ update msg ( model, mobile ) = newG = { g | motor = Motor.default - , harmony = Harmo.newDuration <| Mobile.getLength g mobile.gears + , harmony = + Harmo.newRate + (Mobile.getLength g mobile.gears / CommonData.getWheeledContentLength g) } in { acc | gears = Coll.insert newG acc.gears } @@ -803,13 +761,16 @@ update msg ( model, mobile ) = l = Mobile.getLength g mobile.gears + contentLength = + CommonData.getWheeledContentLength g + collar = case collaring of Simple -> Collar.fromWheel g.wheel l Mult i -> - Collar.fromWheelMult g.wheel i l + Collar.fromWheelMult g.wheel i contentLength Div s i -> Collar.fromSoundDiv s i l @@ -819,8 +780,8 @@ update msg ( model, mobile ) = res = case collaring of - Mult i -> - Harmo.changeSelf id (toFloat i * l) tmp + Mult _ -> + Harmo.changeRate id l contentLength tmp _ -> tmp @@ -2018,11 +1979,12 @@ doResize id d add mobile = newSize = abs <| dd * 2 + length in - { mobile | gears = Harmo.resizeFree id newSize gears } + { mobile | gears = Harmo.resizeFree id newSize (CommonData.getWheeledContentLength <| Coll.get id gears) gears } doChangeContent : Id Geer -> Conteet -> Maybe Float -> Model -> Mobeel -> Return doChangeContent id c mayColor model mobile = + -- TODO probably needs to keep duration instead of rate when changing content let return = { model = model @@ -2085,19 +2047,10 @@ addBead model mobile bead = CommonData.updateWheel ( id, [] ) (Wheel.ChangeContent <| Content.C newCol) mobile - - oldLength = - Mobile.getLengthId id mobile.gears - - oldContentLength = - Collar.getTotalLength col - - newContentLength = - Collar.getTotalLength newCol in Just ( { model | beadCursor = model.beadCursor + 1 } - , { newMob | gears = Harmo.changeSelf id (newContentLength * oldLength / oldContentLength) newMob.gears } + , newMob , id ) @@ -2388,27 +2341,7 @@ manageInteractEvent event model mobile = in case interactWave g event model mobile of Just subMsg -> - let - ret = - update (WheelMsgs [ ( ( id, [] ), subMsg ) ]) ( model, mobile ) - - newMob = - ret.mobile - - oldPercents = - Wheel.getLoopPercents (Coll.get id mobile.gears) - - newPercents = - Wheel.getLoopPercents (Coll.get id newMob.gears) - - ratio = - (Tuple.second newPercents - Tuple.first newPercents) - / (Tuple.second oldPercents - Tuple.first oldPercents) - - oldLength = - Mobile.getLengthId id mobile.gears - in - { ret | mobile = { newMob | gears = Harmo.changeSelf id (ratio * oldLength) newMob.gears } } + update (WheelMsgs [ ( ( id, [] ), subMsg ) ]) ( model, mobile ) Nothing -> return diff --git a/src/Harmony.elm b/src/Harmony.elm index 136fb29..6fad0d6 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -109,8 +109,8 @@ clean id coll = Debug.todo "Clean Base" -changeSelf : Id (Harmonized g) -> Float -> Coll (Harmonized g) -> Coll (Harmonized g) -changeSelf id dur coll = +changeSelfUnit : Id (Harmonized g) -> SelfUnit -> Coll (Harmonized g) -> Coll (Harmonized g) +changeSelfUnit id su coll = let g = Coll.get id coll @@ -120,17 +120,27 @@ changeSelf id dur coll = in case harmo.ref of Self r -> - Coll.update id (always { g | harmony = { harmo | ref = Self { r | unit = Duration dur } } }) coll + Coll.update id (always { g | harmony = { harmo | ref = Self { r | unit = su } } }) coll Other rId -> coll - |> Coll.update id (always { g | harmony = newDuration dur }) + |> Coll.update id (always { g | harmony = newHarmoWithSelfUnit su }) |> Coll.update (Coll.idMap rId) (remove id) -resizeFree : Id (Harmonized g) -> Float -> Coll (Harmonized g) -> Coll (Harmonized g) -resizeFree id length coll = - changeSelf id (length / Fract.toFloat (getHarmo id coll).fract) coll +changeRate : Id (Harmonized g) -> Float -> Float -> Coll (Harmonized g) -> Coll (Harmonized g) +changeRate id newDur contentLength = + changeSelfUnit id <| Rate (newDur / contentLength) + + +changeDuration : Id (Harmonized g) -> Float -> Coll (Harmonized g) -> Coll (Harmonized g) +changeDuration id newDur = + changeSelfUnit id <| Duration newDur + + +resizeFree : Id (Harmonized g) -> Float -> Float -> Coll (Harmonized g) -> Coll (Harmonized g) +resizeFree id length contentLength coll = + changeRate id (length / Fract.toFloat (getHarmo id coll).fract) contentLength coll toContentLength : Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g) @@ -213,14 +223,19 @@ getLength getContentLength el coll = Fract.toFloat harmo.fract * refUnit +newHarmoWithSelfUnit : SelfUnit -> Harmony +newHarmoWithSelfUnit su = + { fract = Fract.integer 1, ref = Self { unit = su, group = [], links = [] } } + + newDuration : Float -> Harmony newDuration d = - { fract = Fract.integer 1, ref = Self { unit = Duration d, group = [], links = [] } } + newHarmoWithSelfUnit <| Duration d newRate : Float -> Harmony newRate r = - { fract = Fract.integer 1, ref = Self { unit = Rate r, group = [], links = [] } } + newHarmoWithSelfUnit <| Rate r hasHarmonics : Harmony -> Bool From e185aaacbaa8993a49ac78de71513fd8f9148275 Mon Sep 17 00:00:00 2001 From: cbossut Date: Wed, 23 Dec 2020 17:36:35 +0100 Subject: [PATCH 07/43] test Mic Latency with https://superpowered.com/webbrowserlatency --- ports.js | 27 ++++++++++++++++++++--- src/Main.elm | 62 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/ports.js b/ports.js index bec75bd..9709d68 100644 --- a/ports.js +++ b/ports.js @@ -69,6 +69,7 @@ let mic , micToRecord , micRecorder , recording +//const micLatency = 0.180 // calc with https://superpowered.com/webbrowserlatency function openMic() { navigator.mediaDevices.getUserMedia({audio : true}).then(stream => { mic = stream @@ -78,16 +79,36 @@ function openMic() { }).catch(console.error) } -function inputRec(name) { +function inputRec(args) { + let name = args[0] + , micLatency = args[1] if (name) { micRecorder.stop() - micRecorder.exportWAV(bl => app.ports.gotNewSample.send(new File([bl], name + ".wav", {type: "audio/wav"}))) + console.log(micLatency/1000) +// micRecorder.exportWAV(bl => app.ports.gotNewSample.send(new File([bl], name + ".wav", {type: "audio/wav"}))) + micRecorder.getBuffer(bs => { + let start = Math.round(ctx.sampleRate * micLatency / 1000) + , length = bs[0].length - start + , newBuf = new AudioBuffer( + { length : length + , numberOfChannels : bs.length + , sampleRate : ctx.sampleRate + } + ) + + for (let i = 0 ; i < bs.length ; i++) { + let chan = bs[i].slice(start) + newBuf.copyToChannel(chan, i) + } + + app.ports.gotNewSample.send(new File([audioBufferToWav(newBuf)], name + ".wav", {type: "audio/wav"})) + }) micRecorder.clear() recording = false if (!scheduler.running) ctx.suspend() } else { if (mic) { - ctx.resume() +// ctx.resume() micRecorder.record() recording = true } else console.error("won’t record mic if it ain’t opened !") diff --git a/src/Main.elm b/src/Main.elm index 2ab3203..4de22b6 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -47,7 +47,7 @@ port openMic : () -> Cmd msg port micOpened : (() -> msg) -> Sub msg -port inputRec : String -> Cmd msg +port inputRec : ( String, Int ) -> Cmd msg port gotNewSample : (D.Value -> msg) -> Sub msg @@ -80,7 +80,7 @@ main = type alias Model = { connected : Bool , currentUrl : Url.Url - , micState : Maybe ( Bool, String ) + , micState : Maybe ( Bool, ( String, Int ) ) -- Recording, FileName, Latency in ms , soundList : Dict String SoundListType , loadedSoundList : List Sound , showDirLoad : Bool @@ -155,8 +155,9 @@ type Msg | RequestOpenMic | MicOpened | StartMicRec - | EndMicRec String + | EndMicRec String Int | EnteredNewRecName String + | EnteredMicLatency String | ClickedUploadSound | UploadSounds File (List File) | GotNewSample (Result D.Error File) @@ -510,20 +511,35 @@ update msg model = ( model, openMic () ) MicOpened -> - ( { model | micState = Just ( False, "" ) }, Cmd.none ) + ( { model | micState = Just ( False, ( "", 0 ) ) }, Cmd.none ) StartMicRec -> ( { model | micState = Maybe.map (Tuple.mapFirst <| always True) model.micState } - , inputRec "" + , inputRec ( "", 0 ) ) - EndMicRec fileName -> + EndMicRec fileName latency -> ( { model | micState = Maybe.map (Tuple.mapFirst <| always False) model.micState } - , inputRec fileName + , inputRec ( fileName, latency ) ) EnteredNewRecName fileName -> - ( { model | micState = Maybe.map (Tuple.mapSecond <| always fileName) model.micState } + ( { model | micState = Maybe.map (Tuple.mapSecond <| Tuple.mapFirst <| always fileName) model.micState } + , Cmd.none + ) + + EnteredMicLatency lat -> + ( { model + | micState = + Maybe.map + (Tuple.mapSecond <| + Tuple.mapSecond <| + always <| + Maybe.withDefault 0 <| + String.toInt lat + ) + model.micState + } , Cmd.none ) @@ -886,16 +902,24 @@ viewSounds model = viewOpenRefreshButtons ClickedUploadSound RequestSoundList model.connected , column [ width fill, spacing 20 ] <| case model.micState of - Just ( False, name ) -> - [ Input.button [] - { onPress = - if String.isEmpty name then - Nothing + Just ( False, ( name, latency ) ) -> + [ row [] + [ Input.button [] + { onPress = + if String.isEmpty name then + Nothing - else - Just StartMicRec - , label = text "Rec Mic" - } + else + Just StartMicRec + , label = text "Rec Mic" + } + , Input.text [ Font.color (rgb 0 0 0), paddingXY 5 0 ] + { text = String.fromInt latency + , placeholder = Nothing + , label = Input.labelHidden "Mic latency in ms" + , onChange = EnteredMicLatency + } + ] , Input.text [ Font.color (rgb 0 0 0), paddingXY 5 0 ] { text = name , placeholder = Just <| Input.placeholder [] <| text "Nom du fichier" @@ -904,8 +928,8 @@ viewSounds model = } ] - Just ( True, name ) -> - [ Input.button [] { onPress = Just <| EndMicRec name, label = text "Stop Mic" } + Just ( True, ( name, latency ) ) -> + [ Input.button [] { onPress = Just <| EndMicRec name latency, label = text "Stop Mic" } , text name ] From 0e16a5a447b9261b2fb0ad8bd7bce50467035658 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 2 Jan 2021 13:49:49 +0100 Subject: [PATCH 08/43] Move cursors in oneSound Collars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finally !!! --- src/Data/Content.elm | 208 ++++++++++++++++++++++++++++++++++++++++++ src/Data/Wheel.elm | 66 +++++++++++++- src/Editor/Mobile.elm | 17 +++- src/Waveform.elm | 3 +- 4 files changed, 291 insertions(+), 3 deletions(-) diff --git a/src/Data/Content.elm b/src/Data/Content.elm index bc138dc..78fd947 100644 --- a/src/Data/Content.elm +++ b/src/Data/Content.elm @@ -110,6 +110,214 @@ type alias Collar item = } + +-- TODO doesn’t updates collar.loop… + + +setCollarLoop : + (( Maybe Float, Maybe Float ) -> Bead item -> Bead item) + -> ( Maybe Float, Maybe Float ) + -> Collar item + -> Collar item +setCollarLoop chgBeadLoop mayPoints c = + case c.oneSound of + Nothing -> + c + + Just one -> + case mayPoints of + ( Nothing, Just end ) -> + let + safeEnd = + clamp one.start 1 end + + newDivs = + List.filter (\x -> x < safeEnd) one.divs + in + if List.isEmpty newDivs then + { c | oneSound = Nothing, matrice = 1, beads = [], head = chgBeadLoop mayPoints c.head } + + else + let + nBeads = + List.length newDivs + + tmpBeads = + List.take nBeads c.beads + + newBeads = + List.indexedMap + (\i -> + if i == nBeads - 1 then + chgBeadLoop mayPoints + + else + identity + ) + tmpBeads + in + { c + | matrice = nBeads + 1 + , beads = newBeads + , oneSound = Just { one | end = safeEnd, divs = newDivs } + } + + ( Just start, Nothing ) -> + let + safeStart = + clamp 0 one.end start + + newDivs = + List.filter (\x -> x > safeStart) one.divs + in + if List.isEmpty newDivs then + { c + | oneSound = Nothing + , matrice = 1 + , beads = [] + , head = chgBeadLoop mayPoints <| getBead (List.length c.beads) c + } + + else + let + nBeads = + List.length newDivs + 1 + + allBeads = + getBeads c + + tmpBeads = + List.drop (List.length allBeads - nBeads) allBeads + in + case tmpBeads of + head :: newBeads -> + { c + | matrice = nBeads + , beads = newBeads + , head = chgBeadLoop mayPoints head + , oneSound = Just { one | start = safeStart, divs = newDivs } + } + + [] -> + let + _ = + Debug.log "Incoherency between oneSound and beads" ( c.oneSound, c.beads ) + in + c + + ( Just start, Just end ) -> + let + safeStart = + clamp 0 1 start + + safeEnd = + clamp safeStart 1 end + + newDivs = + List.map + (\d -> + safeStart + + (d - one.start) + * (safeEnd - safeStart) + / (one.end - one.start) + ) + one.divs + in + { c + | oneSound = Just { one | start = safeStart, end = safeEnd, divs = newDivs } + , head = chgBeadLoop ( Just safeStart, List.head newDivs ) c.head + , beads = List.map3 (\b s e -> chgBeadLoop ( Just s, Just e ) b) c.beads newDivs <| List.drop 1 newDivs ++ [ safeEnd ] + } + + _ -> + c + + +setCollarDiv : + (( Maybe Float, Maybe Float ) -> Bead item -> Bead item) + -> Int + -> Float + -> Collar item + -> Collar item +setCollarDiv chgBeadLoop i percent c = + case c.oneSound of + Nothing -> + c + + Just one -> + case List.head <| List.drop i one.divs of + Nothing -> + c + + Just oldDiv -> + if percent <= one.start then + setCollarLoop chgBeadLoop ( Just percent, Nothing ) c + + else if percent >= one.end then + setCollarLoop chgBeadLoop ( Nothing, Just percent ) c + + else + let + tmpDivs = + List.filter (\x -> (x < percent && x < oldDiv) || (x > percent && x > oldDiv)) one.divs + + ( preDiv, postDiv ) = + List.partition (\x -> x < percent) tmpDivs + + newDivs = + preDiv ++ percent :: postDiv + + oldBeads = + getBeads c + + preBead = + List.take (List.length preDiv) oldBeads + + mayLeftBead = + List.head <| List.drop (List.length preDiv) oldBeads + + nToPost = + List.length oldBeads - List.length postDiv + + mayRightBead = + List.head <| List.drop (nToPost - 1) oldBeads + + postBead = + List.drop nToPost oldBeads + in + case ( mayLeftBead, mayRightBead ) of + ( Just leftBead, Just rightBead ) -> + let + tmpBeads = + preBead + ++ [ chgBeadLoop ( Nothing, Just percent ) leftBead ] + ++ [ chgBeadLoop ( Just percent, Nothing ) rightBead ] + ++ postBead + in + case tmpBeads of + newHead :: newBeads -> + { c + | matrice = List.length tmpBeads + , head = newHead + , beads = newBeads + , oneSound = Just { one | divs = newDivs } + } + + [] -> + let + _ = + Debug.log "Incoherency1 between oneSound and beads" ( c.oneSound, c.beads ) + in + c + + _ -> + let + _ = + Debug.log "Incoherency2 between oneSound and beads" ( c.oneSound, c.beads ) + in + c + + getBeads : Collar item -> List (Bead item) getBeads c = c.head :: c.beads diff --git a/src/Data/Wheel.elm b/src/Data/Wheel.elm index db818e1..5af3a5b 100644 --- a/src/Data/Wheel.elm +++ b/src/Data/Wheel.elm @@ -1,7 +1,7 @@ module Data.Wheel exposing (..) import Color exposing (Color) -import Data.Content as Content exposing (Content, Mobile) +import Data.Content as Content exposing (Bead, Content, Mobile) import Html.Attributes import Interact import Json.Decode as D @@ -58,6 +58,14 @@ getLoopPercents { wheel } = C (Content.S s) -> Sound.getLoopPercents s + C (Content.C c) -> + case c.oneSound of + Just one -> + ( one.start, one.end ) + + _ -> + ( 0, 1 ) + _ -> ( 0, 1 ) @@ -121,6 +129,7 @@ type Msg | Mute Bool | ChangeStart Float | ChangeLoop ( Maybe Float, Maybe Float ) + | ChangeDiv Int Float | Named String | ChangeColor Float | ToggleContentView @@ -131,6 +140,32 @@ update msg g = let wheel = g.wheel + + --TODO Very specific to beads, but content or collar doesn’t know wheels, so where is it to put ? + chgLoopWithSoundLength : ( Maybe Float, Maybe Float ) -> Bead Wheel -> Bead Wheel + chgLoopWithSoundLength p b = + let + w = + b.wheel + + newSound = + case getWheelContent w of + Content.S s -> + Sound.setLoop p s + + _ -> + Sound.noSound + + newWheel = + { w + | startPercent = Tuple.first <| Sound.getLoopPercents newSound + , content = C <| Content.S newSound + } + + length = + Sound.length newSound + in + { wheel = newWheel, length = length } in case msg of ChangeContent c -> @@ -175,6 +210,35 @@ update msg g = } } + C (Content.C c) -> + { g + | wheel = + { wheel + | content = + C <| + Content.C <| + Content.setCollarLoop chgLoopWithSoundLength mayPoints c + } + } + + _ -> + g + + ChangeDiv i percent -> + case wheel.content of + C (Content.C c) -> + { g + | wheel = + { wheel + | content = + C <| + Content.C <| + Debug.log "new" <| + Content.setCollarDiv chgLoopWithSoundLength i percent <| + Debug.log "old" c + } + } + _ -> g diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index ca69ed2..c316013 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -2769,7 +2769,22 @@ interactWave g event model mobile = Just <| Wheel.ChangeStart <| move absD <| g.wheel.startPercent Divide i -> - Nothing + let + mayCollar = + case Wheel.getContent g of + Content.C collar -> + Just collar + + _ -> + Nothing + + mayDivs = + Maybe.map .divs <| Maybe.andThen .oneSound mayCollar + + mayPercent = + Maybe.andThen (List.head << List.drop i) mayDivs + in + mayPercent |> Maybe.andThen (\percent -> Just <| Wheel.ChangeDiv i <| move absD percent) ( IWaveSel, Interact.Dragged { absD } _ _ ) -> let diff --git a/src/Waveform.elm b/src/Waveform.elm index c639ab0..1a985be 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -189,6 +189,7 @@ view wave mayCursors interState wrapInter = CollarDiv c -> [ selection ( toPx 0, toPx c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 , selection ( toPx c.end, toPx 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 + , selection ( toPx c.start, toPx c.end ) (Just IWaveSel) <| rgba 0 0 0 0 , cursor (toPx c.start) LoopStart wave.height , cursor (toPx c.end) LoopEnd wave.height ] @@ -245,7 +246,7 @@ cursor pos cur h = , handle [ centerY, moveUp <| toFloat h / 4 ] ] - Divide int -> + Divide _ -> [ handle [ alignTop, moveUp <| toFloat border ] , handle [ alignBottom, moveDown <| toFloat border ] ] From e7807080a35e5c4a3bd2c15e0dfd33058bddc216 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 2 Jan 2021 13:52:29 +0100 Subject: [PATCH 09/43] Fixes to oneSound collars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don’t erase oneSound property at load Ask for waveForm redraw when select oneSound collar --- src/Data/Content.elm | 20 +++++++++++++++++++- src/Editor/Mobile.elm | 13 +++++++++++-- src/Main.elm | 2 +- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/Data/Content.elm b/src/Data/Content.elm index 78fd947..60fe99a 100644 --- a/src/Data/Content.elm +++ b/src/Data/Content.elm @@ -342,7 +342,25 @@ getBead i c = updateBead : Int -> (Bead item -> Bead item) -> Collar item -> Collar item -updateBead i f c = +updateBead = + updateBeadHelper False + + +updateBeadKeepOneSound : Int -> (Bead item -> Bead item) -> Collar item -> Collar item +updateBeadKeepOneSound = + updateBeadHelper True + + +updateBeadHelper : Bool -> Int -> (Bead item -> Bead item) -> Collar item -> Collar item +updateBeadHelper keepOneSound i f collar = + let + c = + if keepOneSound then + collar + + else + { collar | oneSound = Nothing } + in if i <= 0 then { c | head = f c.head } diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index c316013..ebcf246 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -2556,8 +2556,17 @@ interactSelectEdit event mobile model = in Just ( { model | edit = [ id ], wave = wave }, Cmd.map WaveMsg cmd ) - Content.C _ -> - Just ( { model | edit = [ id ], beadCursor = 0 }, Cmd.none ) + Content.C c -> + case c.oneSound of + Just one -> + let + ( wave, cmd ) = + Waveform.update (Waveform.ChgSound one.soundName) model.wave + in + Just ( { model | edit = [ id ], beadCursor = 0, wave = wave }, Cmd.map WaveMsg cmd ) + + Nothing -> + Just ( { model | edit = [ id ], beadCursor = 0 }, Cmd.none ) _ -> Just ( { model | edit = [ id ] }, Cmd.none ) diff --git a/src/Main.elm b/src/Main.elm index 2ab3203..2014be4 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -369,7 +369,7 @@ update msg model = Just ( collar, sl, cc ) -> case checkLoad (Wheel.getContent b) sl cc of Just ( newContent, newSL, newCmds ) -> - Just ( Collar.updateBead i (Wheel.setContent newContent) collar, newSL, newCmds ) + Just ( Content.updateBeadKeepOneSound i (Wheel.setContent newContent) collar, newSL, newCmds ) _ -> Nothing From 773a1d2419102c0ac7723c670dbc9dbeb1833658 Mon Sep 17 00:00:00 2001 From: cbossut Date: Wed, 6 Jan 2021 15:20:16 +0100 Subject: [PATCH 10/43] sync mic rec stop with mobile stop larach --- src/Main.elm | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Main.elm b/src/Main.elm index a7efc6f..c241f11 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -681,16 +681,22 @@ update msg model = DocMsg subMsg -> let - ( doc, cmd ) = + ( newModel, cmd ) = + case ( model.micState, subMsg, model.doc.editor.tool ) of + -- FIXME Absurd... Should be a commonMsg and common ChangedMode + ( _, Doc.MobileMsg (Editor.ChangedMode (Editor.ChangeSound _)), _ ) -> + ( { model | fileExplorerTab = LoadedSounds }, Cmd.none ) + + ( Just ( True, ( name, latency ) ), Doc.MobileMsg Editor.ToggleEngine, Editor.Play True _ ) -> + update (EndMicRec name latency) model + + _ -> + ( model, Cmd.none ) + + ( doc, subCmd ) = Doc.update subMsg model.doc in - case subMsg of - -- FIXME Absurd... Should be a commonMsg and common ChangedMode - Doc.MobileMsg (Editor.ChangedMode (Editor.ChangeSound _)) -> - ( { model | doc = doc, fileExplorerTab = LoadedSounds }, Cmd.map DocMsg cmd ) - - _ -> - ( { model | doc = doc }, Cmd.map DocMsg cmd ) + ( { newModel | doc = doc }, Cmd.batch <| cmd :: [ Cmd.map DocMsg subCmd ] ) -- TODO Should dispatch KeysMsg, not specific messages to each part, too big of a dependency KeysMsg subMsg -> From c26c52f3f374bce5e1c141075e51c19a73122a17 Mon Sep 17 00:00:00 2001 From: cbossut Date: Mon, 8 Feb 2021 17:42:42 +0100 Subject: [PATCH 11/43] draw zoomed waveform --- ports.js | 11 +++-- src/Editor/Mobile.elm | 4 +- src/Waveform.elm | 106 +++++++++++++++++++++++++++++++++++------- 3 files changed, 98 insertions(+), 23 deletions(-) diff --git a/ports.js b/ports.js index 9709d68..8beae44 100644 --- a/ports.js +++ b/ports.js @@ -19,10 +19,13 @@ function sendSize(entries) { app.ports.newSVGSize.send(entries[0].contentRect) } -function drawSound(soundName) { - if (buffers[soundName]) { - drawSamples(Array.from(buffers[soundName].getChannelData(0))) // TODO mix channels ? - app.ports.soundDrawn.send(soundName) +function drawSound(sv) { + if (buffers[sv.soundName]) { + let buf = buffers[sv.soundName] + , half = Math.round(buf.length / sv.zoomFactor / 2) + , mid = Math.round(buf.length * sv.centerPercent) + drawSamples(Array.from(buffers[sv.soundName].getChannelData(0).slice(mid - half, mid + half))) // TODO mix channels ? + app.ports.soundDrawn.send(sv) } else console.log(soundName + ' isn’t loaded, cannot draw') } diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index ebcf246..c0c188c 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -1121,7 +1121,7 @@ viewContent ( model, mobile ) = in case ( g.wheel.viewContent, Wheel.getContent g ) of ( True, Content.S s ) -> - if model.wave.drawn == (Waveform.SoundDrawn <| Sound.toString s) then + if Waveform.isDrawn model.wave <| Sound.toString s then Just <| Waveform.Sound { offset = g.wheel.startPercent, start = start, end = end } else @@ -1130,7 +1130,7 @@ viewContent ( model, mobile ) = ( True, Content.C c ) -> case c.oneSound of Just oneSound -> - if model.wave.drawn == Waveform.SoundDrawn oneSound.soundName then + if Waveform.isDrawn model.wave oneSound.soundName then Just <| Waveform.CollarDiv { start = oneSound.start diff --git a/src/Waveform.elm b/src/Waveform.elm index 1a985be..3c86158 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -14,12 +14,20 @@ import Json.Decode as D -- TODO Maybe better to compute min max rms in js but draw in elm with adequate lib -port requestSoundDraw : String -> Cmd msg +port requestSoundDraw : SoundView -> Cmd msg port soundDrawn : (D.Value -> msg) -> Sub msg +soundViewDecoder : D.Decoder SoundView +soundViewDecoder = + D.map3 SoundView + (D.field "soundName" D.string) + (D.field "zoomFactor" D.float) + (D.field "centerPercent" D.float) + + canvasId : String canvasId = "waveform" @@ -38,10 +46,17 @@ type alias Waveform = } +type alias SoundView = + { soundName : String + , zoomFactor : Float + , centerPercent : Float + } + + type Drawing = None - | SoundDrawn String - | Pending String + | SoundDrawn SoundView + | Pending SoundView init : Waveform @@ -62,10 +77,21 @@ getSelPercents { sel, size } = Maybe.map (Tuple.mapBoth toPercent toPercent) sel +isDrawn : Waveform -> String -> Bool +isDrawn { drawn } name = + case drawn of + SoundDrawn { soundName } -> + name == soundName + + _ -> + False + + type Msg = GotSize Int | ChgSound String - | GotDrawn (Result D.Error String) + | ChgView Float Float -- Zoom, Center percent + | GotDrawn (Result D.Error SoundView) | Select ( Float, Float ) | MoveSel Float | CancelSel @@ -77,30 +103,76 @@ update msg wave = GotSize size -> ( { wave | size = size - 2 * border } , case wave.drawn of - SoundDrawn s -> - requestSoundDraw s + SoundDrawn sv -> + requestSoundDraw sv _ -> Cmd.none ) - ChgSound s -> - if wave.drawn == SoundDrawn s then - ( wave, Cmd.none ) + ChgSound name -> + let + newSV = + SoundView name 1 0.5 + + chgRes = + ( { wave | drawn = Pending newSV }, requestSoundDraw newSV ) + in + case wave.drawn of + SoundDrawn { soundName } -> + if name == soundName then + ( wave, Cmd.none ) + + else + chgRes + + _ -> + chgRes + + ChgView factor center -> + let + f = + if factor < 1 then + 1 + + else + factor - else - ( { wave | drawn = Pending s }, requestSoundDraw s ) + c = + clamp (1 / f / 2) (1 - 1 / f / 2) center + in + case wave.drawn of + SoundDrawn sv -> + if f /= sv.zoomFactor || c /= sv.centerPercent then + let + newSV = + { sv | zoomFactor = f, centerPercent = c } + in + ( { wave | drawn = Pending newSV }, requestSoundDraw newSV ) + + else + ( wave, Cmd.none ) + + Pending { soundName } -> + let + newSV = + SoundView soundName f c + in + ( { wave | drawn = Pending newSV }, requestSoundDraw newSV ) + + None -> + ( wave, Cmd.none ) GotDrawn res -> case res of - Ok str -> + Ok soundView -> case wave.drawn of - Pending s -> - if s == str then - ( { wave | drawn = SoundDrawn s }, Cmd.none ) + Pending sv -> + if sv == soundView then + ( { wave | drawn = SoundDrawn sv }, Cmd.none ) else - ( wave, requestSoundDraw s ) + ( wave, requestSoundDraw sv ) _ -> ( wave, Cmd.none ) @@ -140,7 +212,7 @@ update msg wave = sub : Sub Msg sub = - soundDrawn (GotDrawn << D.decodeValue D.string) + soundDrawn (GotDrawn << D.decodeValue soundViewDecoder) type Cursors From d1cfa087e1c97fde3f73bfb8a27acd8504178803 Mon Sep 17 00:00:00 2001 From: cbossut Date: Mon, 8 Feb 2021 19:12:20 +0100 Subject: [PATCH 12/43] zoom Wave to point with Wheel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Buggy… --- src/Editor/Mobile.elm | 1 + src/Waveform.elm | 104 ++++++++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index c0c188c..c872fb6 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -1221,6 +1221,7 @@ viewContent ( model, mobile ) = mayWavePoints model.interact InteractMsg + WaveMsg ] <| Element.html <| diff --git a/src/Waveform.elm b/src/Waveform.elm index 3c86158..30eb42a 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -6,6 +6,7 @@ import Element.Background as Bg import Element.Border as Border import Html exposing (canvas) import Html.Attributes as Attr +import Html.Events.Extra.Wheel as Wheel import Interact import Json.Decode as D @@ -91,6 +92,7 @@ type Msg = GotSize Int | ChgSound String | ChgView Float Float -- Zoom, Center percent + | ZoomPoint Float ( Float, Float ) | GotDrawn (Result D.Error SoundView) | Select ( Float, Float ) | MoveSel Float @@ -163,6 +165,27 @@ update msg wave = None -> ( wave, Cmd.none ) + ZoomPoint delta ( x, y ) -> + case wave.drawn of + SoundDrawn sv -> + let + factor = + clamp 0.01 2 <| 1 + delta / 100 + + d = + x / toFloat wave.size - 0.5 + + f = + sv.zoomFactor * factor + + c = + sv.centerPercent + d * (factor - 1) / f + in + update (ChgView f c) wave + + _ -> + ( wave, Cmd.none ) + GotDrawn res -> case res of Ok soundView -> @@ -225,51 +248,54 @@ view : -> Maybe Cursors -> Interact.State Interactable Zone -> (Interact.Msg Interactable Zone -> msg) + -> (Msg -> msg) -> Element msg -view wave mayCursors interState wrapInter = +view wave mayCursors interState wrapInter wrapMsg = let toPx = round << ((*) <| toFloat wave.size) in el - (case mayCursors of - Just cursors -> - (List.map (htmlAttribute << Attr.map wrapInter) <| Interact.dragSpaceEvents interState ZWave) - ++ [ Border.color <| rgb 0 0 0 - , Border.width border - , Bg.color <| rgb 1 1 1 - , alignBottom - ] - ++ (List.map (mapAttribute wrapInter) <| - case cursors of - Sound c -> - [ selection ( toPx 0, toPx c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , selection ( toPx c.end, toPx 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , selection ( toPx c.start, toPx c.end ) (Just IWaveSel) <| rgba 0 0 0 0 - , cursor (toPx c.start) LoopStart wave.height - , cursor (toPx c.end) LoopEnd wave.height - , cursor (toPx c.offset) StartOffset wave.height - ] - ++ (case wave.sel of - Just points -> - [ selection points Nothing <| rgba 0.3 0.3 0.3 0.3 ] - - Nothing -> - [] - ) - - CollarDiv c -> - [ selection ( toPx 0, toPx c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , selection ( toPx c.end, toPx 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , selection ( toPx c.start, toPx c.end ) (Just IWaveSel) <| rgba 0 0 0 0 - , cursor (toPx c.start) LoopStart wave.height - , cursor (toPx c.end) LoopEnd wave.height - ] - ++ List.indexedMap (\i div -> cursor (toPx div) (Divide i) wave.height) c.divs - ) - - Nothing -> - [] + ((htmlAttribute <| Attr.map wrapMsg <| Wheel.onWheel (\e -> ZoomPoint -e.deltaY e.mouseEvent.offsetPos)) + :: (case mayCursors of + Just cursors -> + (List.map (htmlAttribute << Attr.map wrapInter) <| Interact.dragSpaceEvents interState ZWave) + ++ [ Border.color <| rgb 0 0 0 + , Border.width border + , Bg.color <| rgb 1 1 1 + , alignBottom + ] + ++ (List.map (mapAttribute wrapInter) <| + case cursors of + Sound c -> + [ selection ( toPx 0, toPx c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 + , selection ( toPx c.end, toPx 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 + , selection ( toPx c.start, toPx c.end ) (Just IWaveSel) <| rgba 0 0 0 0 + , cursor (toPx c.start) LoopStart wave.height + , cursor (toPx c.end) LoopEnd wave.height + , cursor (toPx c.offset) StartOffset wave.height + ] + ++ (case wave.sel of + Just points -> + [ selection points Nothing <| rgba 0.3 0.3 0.3 0.3 ] + + Nothing -> + [] + ) + + CollarDiv c -> + [ selection ( toPx 0, toPx c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 + , selection ( toPx c.end, toPx 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 + , selection ( toPx c.start, toPx c.end ) (Just IWaveSel) <| rgba 0 0 0 0 + , cursor (toPx c.start) LoopStart wave.height + , cursor (toPx c.end) LoopEnd wave.height + ] + ++ List.indexedMap (\i div -> cursor (toPx div) (Divide i) wave.height) c.divs + ) + + Nothing -> + [] + ) ) <| html <| From 65732612f3941fa455841b05260ceb8335ed6bc6 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sun, 14 Feb 2021 11:44:32 +0100 Subject: [PATCH 13/43] All collars lose mums at creation --- src/Editor/Mobile.elm | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index ebcf246..acf6132 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -777,17 +777,9 @@ update msg ( model, mobile ) = tmp = Coll.update id (Wheel.setContent <| Content.C collar) mobile.gears - - res = - case collaring of - Mult _ -> - Harmo.changeRate id l contentLength tmp - - _ -> - tmp in { return - | mobile = { mobile | gears = res } + | mobile = { mobile | gears = Harmo.changeRate id l contentLength tmp } , toUndo = Do } From 38a9143cbd930bd8193af00c935a018295a48e62 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sun, 14 Feb 2021 14:38:56 +0100 Subject: [PATCH 14/43] Keep Duration when changing content --- src/Editor/Mobile.elm | 9 +++++++-- src/Harmony.elm | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index acf6132..8785f5c 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -1976,7 +1976,6 @@ doResize id d add mobile = doChangeContent : Id Geer -> Conteet -> Maybe Float -> Model -> Mobeel -> Return doChangeContent id c mayColor model mobile = - -- TODO probably needs to keep duration instead of rate when changing content let return = { model = model @@ -1993,8 +1992,14 @@ doChangeContent id c mayColor model mobile = chSound = Wheel.update <| Wheel.ChangeContent c + tmpGears = + Harmo.changeContentKeepLength id + (CommonData.getContentLength c) + (CommonData.getWheeledContentLength <| Coll.get id mobile.gears) + mobile.gears + gears = - List.foldl (\el -> Coll.update el chSound) mobile.gears group + List.foldl (\el -> Coll.update el chSound) tmpGears group newModel = -- TODO Why !!?? diff --git a/src/Harmony.elm b/src/Harmony.elm index 6fad0d6..5e0f01e 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -138,6 +138,21 @@ changeDuration id newDur = changeSelfUnit id <| Duration newDur +changeContentKeepLength : Id (Harmonized g) -> Float -> Float -> Coll (Harmonized g) -> Coll (Harmonized g) +changeContentKeepLength id newContentLength oldContentLength coll = + case (getHarmo id coll).ref of + Self { unit } -> + case unit of + Rate r -> + changeSelfUnit id (Rate <| r * oldContentLength / newContentLength) coll + + _ -> + coll + + _ -> + coll + + resizeFree : Id (Harmonized g) -> Float -> Float -> Coll (Harmonized g) -> Coll (Harmonized g) resizeFree id length contentLength coll = changeRate id (length / Fract.toFloat (getHarmo id coll).fract) contentLength coll From 40a66fab966793f837a46cfad52c23f8d239b542 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sun, 14 Feb 2021 14:40:47 +0100 Subject: [PATCH 15/43] auto uncollar when deleting beads and keep duration --- src/Editor/Mobile.elm | 65 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 8785f5c..c8ba51e 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -472,6 +472,19 @@ update msg ( model, mobile ) = } DeleteWheel ( id, l ) -> + let + tmp = + CommonData.deleteWheel ( id, l ) mobile Mobile.rm Collar.rm + + newMob = + case l of + [] -> + tmp + + _ -> + Maybe.withDefault tmp <| + uncollar ( id, List.take (List.length l - 1) l ) tmp + in { return | model = { model @@ -505,7 +518,7 @@ update msg ( model, mobile ) = } , toUndo = Do , toEngine = [ Engine.stop ] - , mobile = CommonData.deleteWheel ( id, l ) mobile Mobile.rm Collar.rm + , mobile = newMob } EnteredFract isNumerator str -> @@ -785,18 +798,11 @@ update msg ( model, mobile ) = UnCollar id -> let - g = - Coll.get id mobile.gears + mayRes = + uncollar ( id, [] ) mobile in - case Wheel.getContent g of - Content.C col -> - { return - | mobile = Mobile.updateGear id (Wheel.setContent <| Wheel.getContent col.head) mobile - , toUndo = Do - } - - _ -> - return + Maybe.withDefault return <| + Maybe.map (\m -> { return | mobile = m, toUndo = Do }) mayRes EnteredCollarMult str -> case toIntOrEmpty str of @@ -2058,6 +2064,41 @@ addBead model mobile bead = Nothing +uncollar : Identifier -> Mobeel -> Maybe Mobeel +uncollar id m = + let + w = + CommonData.getWheel id m + in + case Wheel.getWheelContent w of + Content.C col -> + if Collar.length col == 1 then + let + newMob = + CommonData.updateWheel id (Wheel.ChangeContent <| Wheel.getContent col.head) m + + gId = + Tuple.first id + + getContentLength = + CommonData.getWheeledContentLength << Coll.get gId << .gears + in + Just <| + { newMob + | gears = + Harmo.changeContentKeepLength gId + (getContentLength newMob) + (getContentLength m) + newMob.gears + } + + else + Nothing + + _ -> + Nothing + + computeCuts : ( Vec2, Vec2 ) -> Coll Geer -> List (Link Geer) computeCuts cut gears = Motor.getAllLinks gears From 1129e748a2808352e900226f4ac6b7a2a2306986 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sun, 14 Feb 2021 14:41:28 +0100 Subject: [PATCH 16/43] minor --- src/Data/Common.elm | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Data/Common.elm b/src/Data/Common.elm index 7c7b6b4..5f9cf36 100644 --- a/src/Data/Common.elm +++ b/src/Data/Common.elm @@ -106,18 +106,6 @@ deleteWheel ( id, l ) mobile gRm bRm = [] -> gRm id mobile - [ i ] -> - case Wheel.getContent <| Coll.get id mobile.gears of - Content.C col -> - Content.updateGear id (Wheel.setContent <| Content.C <| bRm i col) mobile - - _ -> - let - _ = - Debug.log "Wrong identifier to delete bead" ( id, l, mobile ) - in - mobile - i :: rest -> case Wheel.getContent <| Coll.get id mobile.gears of Content.C col -> From d8f9479951670431752b5592dea224c4a77b8710 Mon Sep 17 00:00:00 2001 From: cbossut Date: Wed, 24 Mar 2021 13:24:31 +0100 Subject: [PATCH 17/43] Fix keepOneSound usage in Common.updateBead --- src/Data/Common.elm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Data/Common.elm b/src/Data/Common.elm index 5f9cf36..6e41e23 100644 --- a/src/Data/Common.elm +++ b/src/Data/Common.elm @@ -122,19 +122,19 @@ deleteWheel ( id, l ) mobile gRm bRm = updateWheel : Identifier -> Wheel.Msg -> Mobile Wheel -> Mobile Wheel updateWheel ( id, list ) msg m = let - modify = + ( modify, upBead ) = case msg of Wheel.ChangeContent _ -> - True + ( True, Content.updateBead ) Wheel.ChangeStart _ -> - True + ( True, Content.updateBead ) Wheel.ChangeLoop _ -> - True + ( True, Content.updateBead ) _ -> - False + ( False, Content.updateBeadKeepOneSound ) rec : List Int -> Wheel -> Wheel rec l w = @@ -148,7 +148,7 @@ updateWheel ( id, list ) msg m = Content.C col -> let upCol = - Content.updateBead i (\bead -> { bead | wheel = rec ll bead.wheel }) col + upBead i (\bead -> { bead | wheel = rec ll bead.wheel }) col newCol = if modify then From 23c2d40e1375dc8b8ca4f66858055a8c6b1576f6 Mon Sep 17 00:00:00 2001 From: cbossut Date: Wed, 24 Mar 2021 17:32:11 +0100 Subject: [PATCH 18/43] keep rate on beads when collaring collar is ContentLengthed --- src/Data/Collar.elm | 2 +- src/Editor/Mobile.elm | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Data/Collar.elm b/src/Data/Collar.elm index 876e5ed..d05b304 100644 --- a/src/Data/Collar.elm +++ b/src/Data/Collar.elm @@ -72,7 +72,7 @@ fromSoundDiv s d l = List.map Content.S sounds beads = - List.map beadFromContent contents + List.map (\c -> { length = l / toFloat d, wheel = Wheel.fromContent c }) contents in case beads of head :: rest -> diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index c8ba51e..1ef3a0c 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -774,16 +774,13 @@ update msg ( model, mobile ) = l = Mobile.getLength g mobile.gears - contentLength = - CommonData.getWheeledContentLength g - collar = case collaring of Simple -> Collar.fromWheel g.wheel l Mult i -> - Collar.fromWheelMult g.wheel i contentLength + Collar.fromWheelMult g.wheel i l Div s i -> Collar.fromSoundDiv s i l @@ -792,7 +789,7 @@ update msg ( model, mobile ) = Coll.update id (Wheel.setContent <| Content.C collar) mobile.gears in { return - | mobile = { mobile | gears = Harmo.changeRate id l contentLength tmp } + | mobile = { mobile | gears = Harmo.toContentLength id tmp } , toUndo = Do } From 0dbfa900d3c232113483efce5f1a9bdb43e14254 Mon Sep 17 00:00:00 2001 From: cbossut Date: Wed, 24 Mar 2021 17:35:31 +0100 Subject: [PATCH 19/43] Restored good usage of loop in collar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even if unused… --- src/Data/Collar.elm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Data/Collar.elm b/src/Data/Collar.elm index d05b304..9341188 100644 --- a/src/Data/Collar.elm +++ b/src/Data/Collar.elm @@ -42,7 +42,7 @@ beadName i collar = fromWheel : Wheel -> Float -> Colleer fromWheel w l = { matrice = 1 - , loop = l + , loop = 0 , head = { length = l, wheel = w } , beads = [] , oneSound = Nothing @@ -52,7 +52,7 @@ fromWheel w l = fromWheelMult : Wheel -> Int -> Float -> Colleer fromWheelMult w m l = { matrice = m - , loop = l * toFloat m + , loop = 0 , head = { length = l, wheel = w } , beads = List.repeat (m - 1) { length = l, wheel = w } , oneSound = Nothing @@ -77,7 +77,7 @@ fromSoundDiv s d l = case beads of head :: rest -> { matrice = d - , loop = l + , loop = 0 , head = head , beads = rest , oneSound = From ff1f9099c95cda67f6e007044a7bfc014f437a1f Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 27 Mar 2021 10:48:18 +0100 Subject: [PATCH 20/43] Refactor, fix 'n' simplify wave zoom Change centerPercent for startPercent Only take care of soundName for drawn state --- ports.js | 8 ++-- src/Waveform.elm | 116 +++++++++++++++++++++-------------------------- 2 files changed, 55 insertions(+), 69 deletions(-) diff --git a/ports.js b/ports.js index 8beae44..5f3825b 100644 --- a/ports.js +++ b/ports.js @@ -22,10 +22,10 @@ function sendSize(entries) { function drawSound(sv) { if (buffers[sv.soundName]) { let buf = buffers[sv.soundName] - , half = Math.round(buf.length / sv.zoomFactor / 2) - , mid = Math.round(buf.length * sv.centerPercent) - drawSamples(Array.from(buffers[sv.soundName].getChannelData(0).slice(mid - half, mid + half))) // TODO mix channels ? - app.ports.soundDrawn.send(sv) + , size = Math.round(buf.length / sv.zoomFactor) + , start = Math.round(buf.length * sv.startPercent) + drawSamples(Array.from(buffers[sv.soundName].getChannelData(0).slice(start, start + size))) // TODO mix channels ? + app.ports.soundDrawn.send(sv.soundName) } else console.log(soundName + ' isn’t loaded, cannot draw') } diff --git a/src/Waveform.elm b/src/Waveform.elm index 30eb42a..737f34c 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -21,14 +21,6 @@ port requestSoundDraw : SoundView -> Cmd msg port soundDrawn : (D.Value -> msg) -> Sub msg -soundViewDecoder : D.Decoder SoundView -soundViewDecoder = - D.map3 SoundView - (D.field "soundName" D.string) - (D.field "zoomFactor" D.float) - (D.field "centerPercent" D.float) - - canvasId : String canvasId = "waveform" @@ -44,20 +36,22 @@ type alias Waveform = , height : Int , drawn : Drawing , sel : Maybe ( Int, Int ) + , zoomFactor : Float + , startPercent : Float } type alias SoundView = { soundName : String , zoomFactor : Float - , centerPercent : Float + , startPercent : Float } type Drawing = None - | SoundDrawn SoundView - | Pending SoundView + | SoundDrawn String + | Pending String init : Waveform @@ -66,6 +60,8 @@ init = , height = 150 , drawn = None , sel = Nothing + , zoomFactor = 1 + , startPercent = 0 } @@ -81,7 +77,7 @@ getSelPercents { sel, size } = isDrawn : Waveform -> String -> Bool isDrawn { drawn } name = case drawn of - SoundDrawn { soundName } -> + SoundDrawn soundName -> name == soundName _ -> @@ -93,7 +89,7 @@ type Msg | ChgSound String | ChgView Float Float -- Zoom, Center percent | ZoomPoint Float ( Float, Float ) - | GotDrawn (Result D.Error SoundView) + | GotDrawn (Result D.Error String) | Select ( Float, Float ) | MoveSel Float | CancelSel @@ -105,8 +101,8 @@ update msg wave = GotSize size -> ( { wave | size = size - 2 * border } , case wave.drawn of - SoundDrawn sv -> - requestSoundDraw sv + SoundDrawn name -> + requestSoundDraw <| SoundView name wave.zoomFactor wave.startPercent _ -> Cmd.none @@ -114,14 +110,19 @@ update msg wave = ChgSound name -> let - newSV = - SoundView name 1 0.5 + f = + init.zoomFactor + + a = + init.startPercent chgRes = - ( { wave | drawn = Pending newSV }, requestSoundDraw newSV ) + ( { wave | drawn = Pending name, zoomFactor = f, startPercent = a } + , requestSoundDraw <| SoundView name f a + ) in case wave.drawn of - SoundDrawn { soundName } -> + SoundDrawn soundName -> if name == soundName then ( wave, Cmd.none ) @@ -131,71 +132,56 @@ update msg wave = _ -> chgRes - ChgView factor center -> + ChgView factor start -> let f = - if factor < 1 then - 1 + clamp 1 (1 / 0) factor - else - factor + a = + clamp 0 (1 - 1 / f) start + + newWave = + { wave | zoomFactor = f, startPercent = a } - c = - clamp (1 / f / 2) (1 - 1 / f / 2) center + cmd soundName = + requestSoundDraw <| SoundView soundName f a in case wave.drawn of - SoundDrawn sv -> - if f /= sv.zoomFactor || c /= sv.centerPercent then - let - newSV = - { sv | zoomFactor = f, centerPercent = c } - in - ( { wave | drawn = Pending newSV }, requestSoundDraw newSV ) + SoundDrawn name -> + ( newWave, cmd name ) - else - ( wave, Cmd.none ) - - Pending { soundName } -> - let - newSV = - SoundView soundName f c - in - ( { wave | drawn = Pending newSV }, requestSoundDraw newSV ) + Pending name -> + ( newWave, cmd name ) None -> - ( wave, Cmd.none ) + ( newWave, Cmd.none ) ZoomPoint delta ( x, y ) -> - case wave.drawn of - SoundDrawn sv -> - let - factor = - clamp 0.01 2 <| 1 + delta / 100 - - d = - x / toFloat wave.size - 0.5 + let + factor = + clamp 0.01 2 <| 1 + delta / 100 - f = - sv.zoomFactor * factor + f = + wave.zoomFactor * factor - c = - sv.centerPercent + d * (factor - 1) / f - in - update (ChgView f c) wave + d = + x / toFloat wave.size - _ -> - ( wave, Cmd.none ) + a = + wave.startPercent + d * (factor - 1) / f + in + update (ChgView f a) wave GotDrawn res -> case res of - Ok soundView -> + Ok soundName -> case wave.drawn of - Pending sv -> - if sv == soundView then - ( { wave | drawn = SoundDrawn sv }, Cmd.none ) + Pending name -> + if name == soundName then + ( { wave | drawn = SoundDrawn name }, Cmd.none ) else - ( wave, requestSoundDraw sv ) + ( wave, requestSoundDraw <| SoundView name wave.zoomFactor wave.startPercent ) _ -> ( wave, Cmd.none ) @@ -235,7 +221,7 @@ update msg wave = sub : Sub Msg sub = - soundDrawn (GotDrawn << D.decodeValue soundViewDecoder) + soundDrawn (GotDrawn << D.decodeValue D.string) type Cursors From f03efffbe966720d3cedddb95037885ff6bef49d Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 27 Mar 2021 11:21:56 +0100 Subject: [PATCH 21/43] Fix zoomPoint and Wheel event in WaveForm make it same as panSVG --- elm.json | 1 + src/Waveform.elm | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/elm.json b/elm.json index 622ba4a..cf5b6e1 100644 --- a/elm.json +++ b/elm.json @@ -9,6 +9,7 @@ "Chadtech/id": "4.2.0", "NoRedInk/elm-json-decode-pipeline": "1.0.0", "avh4/elm-color": "1.0.0", + "debois/elm-dom": "1.3.0", "elm/browser": "1.0.2", "elm/core": "1.0.2", "elm/file": "1.0.5", diff --git a/src/Waveform.elm b/src/Waveform.elm index 737f34c..88daf12 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -1,12 +1,13 @@ port module Waveform exposing (..) +import DOM import Editor.Interacting exposing (..) import Element exposing (..) import Element.Background as Bg import Element.Border as Border import Html exposing (canvas) import Html.Attributes as Attr -import Html.Events.Extra.Wheel as Wheel +import Html.Events as Events import Interact import Json.Decode as D @@ -88,7 +89,7 @@ type Msg = GotSize Int | ChgSound String | ChgView Float Float -- Zoom, Center percent - | ZoomPoint Float ( Float, Float ) + | ZoomPoint Float Float -- wheelDelta, xOffset | GotDrawn (Result D.Error String) | Select ( Float, Float ) | MoveSel Float @@ -156,19 +157,19 @@ update msg wave = None -> ( newWave, Cmd.none ) - ZoomPoint delta ( x, y ) -> + ZoomPoint delta x -> let factor = - clamp 0.01 2 <| 1 + delta / 100 + clamp 0.01 2 <| 1 + delta / 1000 f = - wave.zoomFactor * factor + wave.zoomFactor / factor d = x / toFloat wave.size a = - wave.startPercent + d * (factor - 1) / f + wave.startPercent + d * (1 / factor - 1) / f in update (ChgView f a) wave @@ -242,7 +243,14 @@ view wave mayCursors interState wrapInter wrapMsg = round << ((*) <| toFloat wave.size) in el - ((htmlAttribute <| Attr.map wrapMsg <| Wheel.onWheel (\e -> ZoomPoint -e.deltaY e.mouseEvent.offsetPos)) + ((htmlAttribute <| + Attr.map wrapMsg <| + Events.on "wheel" <| + D.map3 (\deltaY clientX rect -> ZoomPoint deltaY <| clientX - rect.left - toFloat border) + (D.field "deltaY" D.float) + (D.field "clientX" D.float) + (DOM.currentTarget DOM.boundingClientRect) + ) :: (case mayCursors of Just cursors -> (List.map (htmlAttribute << Attr.map wrapInter) <| Interact.dragSpaceEvents interState ZWave) From ca95fda0fbb8f880613cd18df814f6767cdbfecb Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 27 Mar 2021 16:04:03 +0100 Subject: [PATCH 22/43] wave zoom takes care of cursors and sels --- src/Editor/Mobile.elm | 2 +- src/Waveform.elm | 227 +++++++++++++++++++++++++++++++----------- 2 files changed, 172 insertions(+), 57 deletions(-) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index c872fb6..8ec8d46 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -2756,7 +2756,7 @@ interactWave : Geer -> Interact.Event Interactable Zone -> Model -> Mobeel -> Ma interactWave g event model mobile = let move d val = - val + (Vec.getX d / toFloat model.wave.size) + val + (Waveform.pxToSoundDist model.wave <| round <| Vec.getX d) in case ( event.item, event.action ) of ( IWaveCursor cur, Interact.Dragged { absD } _ _ ) -> diff --git a/src/Waveform.elm b/src/Waveform.elm index 88daf12..9a08dac 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -36,7 +36,7 @@ type alias Waveform = { size : Int , height : Int , drawn : Drawing - , sel : Maybe ( Int, Int ) + , sel : Maybe ( Float, Float ) , zoomFactor : Float , startPercent : Float } @@ -67,12 +67,8 @@ init = getSelPercents : Waveform -> Maybe ( Float, Float ) -getSelPercents { sel, size } = - let - toPercent px = - toFloat px / toFloat size - in - Maybe.map (Tuple.mapBoth toPercent toPercent) sel +getSelPercents { sel } = + sel isDrawn : Waveform -> String -> Bool @@ -143,19 +139,8 @@ update msg wave = newWave = { wave | zoomFactor = f, startPercent = a } - - cmd soundName = - requestSoundDraw <| SoundView soundName f a in - case wave.drawn of - SoundDrawn name -> - ( newWave, cmd name ) - - Pending name -> - ( newWave, cmd name ) - - None -> - ( newWave, Cmd.none ) + ( newWave, requestRedraw newWave ) ZoomPoint delta x -> let @@ -196,22 +181,59 @@ update msg wave = Select ( centerPx, percentLength ) -> let - halfSel = - round (percentLength * toFloat wave.size / 2) + tmpPV = + pxToSoundDist wave wave.size + + ( newWave, percentView ) = + if percentLength > tmpPV then + ( { wave + | zoomFactor = 1 / percentLength + , startPercent = clamp 0 (1 - percentLength) <| wave.startPercent - (percentLength - tmpPV) / 2 + } + , percentLength + ) + + else + ( wave, tmpPV ) + + half = + percentLength / 2 + + ( vp1, vp2 ) = + ( newWave.startPercent, newWave.startPercent + percentView ) safeCenter = - clamp halfSel (wave.size - halfSel) <| round centerPx + clamp (vp1 + half) (vp2 - half) <| pxToSoundPercent newWave <| round centerPx in - ( { wave | sel = Just ( safeCenter - halfSel, safeCenter + halfSel ) }, Cmd.none ) + ( { newWave | sel = Just ( safeCenter - half, safeCenter + half ) }, requestRedraw newWave ) - MoveSel d -> + MoveSel pxD -> case wave.sel of - Just ( px1, px2 ) -> + Just ( p1, p2 ) -> let + ( vp1, vp2 ) = + ( wave.startPercent, wave.startPercent + pxToSoundDist wave wave.size ) + move = - (+) <| clamp -px1 (wave.size - px2) <| round d + (+) <| clamp -p1 (1 - p2) <| pxToSoundDist wave <| round pxD + + ( newP1, newP2 ) = + ( move p1, move p2 ) + + newStartPercent = + if p1 < vp1 then + p1 + + else if p2 > vp2 then + vp1 + p2 - vp2 + + else + vp1 + + newWave = + { wave | sel = Just ( newP1, newP2 ), startPercent = newStartPercent } in - ( { wave | sel = Just ( move px1, move px2 ) }, Cmd.none ) + ( newWave, requestRedraw newWave ) Nothing -> ( wave, Cmd.none ) @@ -239,8 +261,30 @@ view : -> Element msg view wave mayCursors interState wrapInter wrapMsg = let - toPx = - round << ((*) <| toFloat wave.size) + curs pos = + let + p = + soundPercentToViewPercent wave pos + in + if p >= 0 && p <= 1 then + List.singleton << (cursor wave.height <| viewPercentToPx wave p) + + else + always [] + + sel tup foo bar = + let + clmapx = + viewPercentToPx wave << clamp 0 1 << soundPercentToViewPercent wave + + ( a, b ) = + Tuple.mapBoth clmapx clmapx tup + in + if a >= b then + [] + + else + [ selection ( a, b ) foo bar ] in el ((htmlAttribute <| @@ -262,29 +306,30 @@ view wave mayCursors interState wrapInter wrapMsg = ++ (List.map (mapAttribute wrapInter) <| case cursors of Sound c -> - [ selection ( toPx 0, toPx c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , selection ( toPx c.end, toPx 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , selection ( toPx c.start, toPx c.end ) (Just IWaveSel) <| rgba 0 0 0 0 - , cursor (toPx c.start) LoopStart wave.height - , cursor (toPx c.end) LoopEnd wave.height - , cursor (toPx c.offset) StartOffset wave.height - ] - ++ (case wave.sel of - Just points -> - [ selection points Nothing <| rgba 0.3 0.3 0.3 0.3 ] - - Nothing -> - [] - ) + List.concat + [ sel ( 0, c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 + , sel ( c.end, 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 + , sel ( c.start, c.end ) (Just IWaveSel) <| rgba 0 0 0 0 + , curs c.start LoopStart + , curs c.end LoopEnd + , curs c.offset StartOffset + , case wave.sel of + Just points -> + sel points Nothing <| rgba 0.3 0.3 0.3 0.3 + + Nothing -> + [] + ] CollarDiv c -> - [ selection ( toPx 0, toPx c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , selection ( toPx c.end, toPx 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , selection ( toPx c.start, toPx c.end ) (Just IWaveSel) <| rgba 0 0 0 0 - , cursor (toPx c.start) LoopStart wave.height - , cursor (toPx c.end) LoopEnd wave.height - ] - ++ List.indexedMap (\i div -> cursor (toPx div) (Divide i) wave.height) c.divs + List.concat <| + [ sel ( 0, c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 + , sel ( c.end, 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 + , sel ( c.start, c.end ) (Just IWaveSel) <| rgba 0 0 0 0 + , curs c.start LoopStart + , curs c.end LoopEnd + ] + ++ List.indexedMap (\i div -> curs div (Divide i)) c.divs ) Nothing -> @@ -302,18 +347,38 @@ view wave mayCursors interState wrapInter wrapMsg = [] -cursor : Int -> Cursor -> Int -> Attribute (Interact.Msg Interactable zone) -cursor pos cur h = +requestRedraw : Waveform -> Cmd msg +requestRedraw wave = let + cmd soundName = + requestSoundDraw <| SoundView soundName wave.zoomFactor wave.startPercent + in + case wave.drawn of + SoundDrawn name -> + cmd name + + Pending name -> + cmd name + + None -> + Cmd.none + + +cursor : Int -> Int -> Cursor -> Attribute (Interact.Msg Interactable zone) +cursor h pos cur = + let + size = + 8 + handle attrs = inFront <| el ([ htmlAttribute <| Attr.style "cursor" "grab" - , height <| px <| border * 8 - , width <| px <| border * 8 - , Border.rounded <| border * 4 + , height <| px <| border * size + , width <| px <| border * size + , Border.rounded <| border * size // 2 , Bg.color <| rgb 0 0 0 - , moveLeft <| toFloat <| border * 4 + , moveLeft <| toFloat <| border * size // 2 ] ++ attrs ) @@ -367,3 +432,53 @@ selection ( a, b ) mayInter color = ) ) none + + +soundPercentToViewPercent : Waveform -> Float -> Float +soundPercentToViewPercent wave p = + (p - wave.startPercent) * wave.zoomFactor + + +soundDistToViewDist : Waveform -> Float -> Float +soundDistToViewDist wave d = + d * wave.zoomFactor + + +viewPercentToSoundPercent : Waveform -> Float -> Float +viewPercentToSoundPercent wave p = + p / wave.zoomFactor + wave.startPercent + + +viewDistToSoundDist : Waveform -> Float -> Float +viewDistToSoundDist wave d = + d / wave.zoomFactor + + +viewPercentToPx : Waveform -> Float -> Int +viewPercentToPx wave p = + round <| p * toFloat wave.size + + +pxToViewPercent : Waveform -> Int -> Float +pxToViewPercent wave p = + toFloat p / toFloat wave.size + + +soundPercentToPx : Waveform -> Float -> Int +soundPercentToPx w = + soundPercentToViewPercent w >> viewPercentToPx w + + +pxToSoundPercent : Waveform -> Int -> Float +pxToSoundPercent w = + pxToViewPercent w >> viewPercentToSoundPercent w + + +soundDistToPx : Waveform -> Float -> Int +soundDistToPx w = + soundDistToViewDist w >> viewPercentToPx w + + +pxToSoundDist : Waveform -> Int -> Float +pxToSoundDist w = + pxToViewPercent w >> viewDistToSoundDist w From 46d5282e0e6251a48354565249e21611a4735b5b Mon Sep 17 00:00:00 2001 From: cbossut Date: Sun, 28 Mar 2021 12:09:08 +0200 Subject: [PATCH 23/43] waveForm miniMap --- ports.js | 93 ++++++----- src/Editor/Interacting.elm | 17 +- src/Editor/Mobile.elm | 85 +++++++--- src/Waveform.elm | 332 ++++++++++++++++++++++++++----------- 4 files changed, 360 insertions(+), 167 deletions(-) diff --git a/ports.js b/ports.js index 5f3825b..6378253 100644 --- a/ports.js +++ b/ports.js @@ -20,13 +20,18 @@ function sendSize(entries) { } function drawSound(sv) { - if (buffers[sv.soundName]) { - let buf = buffers[sv.soundName] - , size = Math.round(buf.length / sv.zoomFactor) + let buf = buffers[sv.soundName] + if (buf) { + let size = Math.round(buf.length / sv.zoomFactor) , start = Math.round(buf.length * sv.startPercent) - drawSamples(Array.from(buffers[sv.soundName].getChannelData(0).slice(start, start + size))) // TODO mix channels ? + , drawFunc = () => { // TODO mix channels ? + drawSamples('waveform', Array.from(buf.getChannelData(0).slice(start, start + size))) + if (sv.waveformMap) drawSamples('waveformMap', Array.from(buf.getChannelData(0))) + } + if (sv.wait) setTimeout(drawFunc, 10) + else drawFunc() app.ports.soundDrawn.send(sv.soundName) - } else console.log(soundName + ' isn’t loaded, cannot draw') + } else console.log(sv.soundName + ' isn’t loaded, cannot draw') } function loadSound(soundName) { @@ -175,53 +180,51 @@ function engine(o) { } } -function drawSamples(samples) { - setTimeout(() => { - let canvas = document.getElementById('waveform') - , ctx = canvas.getContext('2d') - , {width, height} = canvas - , pxPerSample = width / samples.length +function drawSamples(id, samples) { + let canvas = document.getElementById(id) + , ctx = canvas.getContext('2d') + , {width, height} = canvas + , pxPerSample = width / samples.length + + ctx.clearRect(0, 0, width, height) + + ctx.strokeStyle = 'black' + ctx.beginPath() + ctx.moveTo(0, height / 2) + ctx.lineTo(width, height / 2) + ctx.stroke() - ctx.clearRect(0, 0, width, height) + ctx.strokeRect(0, 0, width, height) + if (pxPerSample < 0.5) { + for (let x = 0 ; x < width ; x++) { + let px = samples.slice(Math.floor(x / pxPerSample), Math.floor((x + 1) / pxPerSample)) + , minPoint = (Math.min.apply(null, px) + 1) * height / 2 + , maxPoint = (Math.max.apply(null, px) + 1) * height / 2 ctx.strokeStyle = 'black' ctx.beginPath() - ctx.moveTo(0, height / 2) - ctx.lineTo(width, height / 2) + ctx.moveTo(x, minPoint) + ctx.lineTo(x, maxPoint) ctx.stroke() - ctx.strokeRect(0, 0, width, height) - - if (pxPerSample < 0.5) { - for (let x = 0 ; x < width ; x++) { - let px = samples.slice(Math.floor(x / pxPerSample), Math.floor((x + 1) / pxPerSample)) - , minPoint = (Math.min.apply(null, px) + 1) * height / 2 - , maxPoint = (Math.max.apply(null, px) + 1) * height / 2 - ctx.strokeStyle = 'black' + let rms = Math.sqrt(px.reduce((acc,v,i,a) => acc + Math.pow(v, 2)) / px.length) + , minRmsPoint = (1 - rms) * height / 2 + , maxRmsPoint = (1 + rms) * height / 2 + if (minRmsPoint > minPoint && maxRmsPoint < maxPoint) { + ctx.strokeStyle = 'gray' ctx.beginPath() - ctx.moveTo(x, minPoint) - ctx.lineTo(x, maxPoint) + ctx.moveTo(x, minRmsPoint) + ctx.lineTo(x, maxRmsPoint) ctx.stroke() - - let rms = Math.sqrt(px.reduce((acc,v,i,a) => acc + Math.pow(v, 2)) / px.length) - , minRmsPoint = (1 - rms) * height / 2 - , maxRmsPoint = (1 + rms) * height / 2 - if (minRmsPoint > minPoint && maxRmsPoint < maxPoint) { - ctx.strokeStyle = 'gray' - ctx.beginPath() - ctx.moveTo(x, minRmsPoint) - ctx.lineTo(x, maxRmsPoint) - ctx.stroke() - } - } - } else { - ctx.strokeStyle = 'black' - ctx.beginPath() - ctx.moveTo(0, (samples[0] + 1) * height / 2) - for (let i = 1 ; i < samples.length ; i++) { - ctx.lineTo(i * pxPerSample, (samples[i] + 1) * height / 2) - } - ctx.stroke() } - }, 10) + } + } else { + ctx.strokeStyle = 'black' + ctx.beginPath() + ctx.moveTo(0, (samples[0] + 1) * height / 2) + for (let i = 1 ; i < samples.length ; i++) { + ctx.lineTo(i * pxPerSample, (samples[i] + 1) * height / 2) + } + ctx.stroke() + } } diff --git a/src/Editor/Interacting.elm b/src/Editor/Interacting.elm index bb20542..8ef9da9 100644 --- a/src/Editor/Interacting.elm +++ b/src/Editor/Interacting.elm @@ -18,16 +18,25 @@ type Interactable | ISound Sound | IWaveCursor Cursor | IWaveSel + | IWaveMapSel type Zone = ZSurface | ZPack | ZWave + | ZWaveMap + + +type WavePart + = Mini + | Main type Cursor - = LoopStart - | LoopEnd - | StartOffset - | Divide Int + = LoopStart WavePart + | LoopEnd WavePart + | StartOffset WavePart + | Divide Int WavePart + | ViewStart + | ViewEnd diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 8ec8d46..175c681 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -989,7 +989,16 @@ update msg ( model, mobile ) = startZone = Tuple.second info.start in - if startZone == ZWave || dragZone == ZWave then + if + startZone + == ZWave + || dragZone + == ZWave + || startZone + == ZWaveMap + || dragZone + == ZWaveMap + then e else @@ -2341,9 +2350,12 @@ manageInteractEvent event model mobile = Coll.get id mobile.gears in case interactWave g event model mobile of - Just subMsg -> + Just (ReturnWheel subMsg) -> update (WheelMsgs [ ( ( id, [] ), subMsg ) ]) ( model, mobile ) + Just (ReturnWave subMsg) -> + update (WaveMsg subMsg) ( model, mobile ) + Nothing -> return @@ -2752,33 +2764,51 @@ interactMove event model mobile = Nothing -interactWave : Geer -> Interact.Event Interactable Zone -> Model -> Mobeel -> Maybe Wheel.Msg +type InteractWaveReturn + = ReturnWheel Wheel.Msg + | ReturnWave Waveform.Msg + + +interactWave : Geer -> Interact.Event Interactable Zone -> Model -> Mobeel -> Maybe InteractWaveReturn interactWave g event model mobile = let - move d val = + move part = + case part of + Main -> + mainMove + + Mini -> + miniMove + + mainMove d val = val + (Waveform.pxToSoundDist model.wave <| round <| Vec.getX d) + + miniMove d val = + val + (Waveform.mapPxToSoundPercent model.wave <| round <| Vec.getX d) in case ( event.item, event.action ) of ( IWaveCursor cur, Interact.Dragged { absD } _ _ ) -> case cur of - LoopEnd -> + LoopEnd part -> Just <| - Wheel.ChangeLoop - ( Nothing - , Just <| move absD <| Tuple.second <| Wheel.getLoopPercents g - ) + ReturnWheel <| + Wheel.ChangeLoop + ( Nothing + , Just <| move part absD <| Tuple.second <| Wheel.getLoopPercents g + ) - LoopStart -> + LoopStart part -> Just <| - Wheel.ChangeLoop - ( Just <| move absD <| Tuple.first <| Wheel.getLoopPercents g - , Nothing - ) + ReturnWheel <| + Wheel.ChangeLoop + ( Just <| move part absD <| Tuple.first <| Wheel.getLoopPercents g + , Nothing + ) - StartOffset -> - Just <| Wheel.ChangeStart <| move absD <| g.wheel.startPercent + StartOffset part -> + Just <| ReturnWheel <| Wheel.ChangeStart <| move part absD <| g.wheel.startPercent - Divide i -> + Divide i part -> let mayCollar = case Wheel.getContent g of @@ -2794,14 +2824,29 @@ interactWave g event model mobile = mayPercent = Maybe.andThen (List.head << List.drop i) mayDivs in - mayPercent |> Maybe.andThen (\percent -> Just <| Wheel.ChangeDiv i <| move absD percent) + mayPercent + |> Maybe.map + (\percent -> + ReturnWheel <| + Wheel.ChangeDiv i <| + move part absD percent + ) + + ViewStart -> + Just <| ReturnWave <| Waveform.MoveStartPercent <| round <| Vec.getX absD + + ViewEnd -> + Just <| ReturnWave <| Waveform.MoveEndPercent <| round <| Vec.getX absD ( IWaveSel, Interact.Dragged { absD } _ _ ) -> let mv = - Just << move absD + Just << mainMove absD in - Just <| Wheel.ChangeLoop <| Tuple.mapBoth mv mv <| Wheel.getLoopPercents g + Just <| ReturnWheel <| Wheel.ChangeLoop <| Tuple.mapBoth mv mv <| Wheel.getLoopPercents g + + ( IWaveMapSel, Interact.Dragged { absD } _ _ ) -> + Just <| ReturnWave <| Waveform.MoveView <| round <| Vec.getX absD _ -> Nothing diff --git a/src/Waveform.elm b/src/Waveform.elm index 9a08dac..f9056d8 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -16,7 +16,7 @@ import Json.Decode as D -- TODO Maybe better to compute min max rms in js but draw in elm with adequate lib -port requestSoundDraw : SoundView -> Cmd msg +port requestSoundDraw : SoundRequest -> Cmd msg port soundDrawn : (D.Value -> msg) -> Sub msg @@ -27,14 +27,28 @@ canvasId = "waveform" +mapCanvasId : String +mapCanvasId = + "waveformMap" + + border : Int border = 2 +waveHeight : Int +waveHeight = + 150 + + +mapHeight : Int +mapHeight = + 50 + + type alias Waveform = { size : Int - , height : Int , drawn : Drawing , sel : Maybe ( Float, Float ) , zoomFactor : Float @@ -42,10 +56,12 @@ type alias Waveform = } -type alias SoundView = +type alias SoundRequest = { soundName : String , zoomFactor : Float , startPercent : Float + , wait : Bool + , waveformMap : Bool } @@ -58,7 +74,6 @@ type Drawing init : Waveform init = { size = 1000 - , height = 150 , drawn = None , sel = Nothing , zoomFactor = 1 @@ -85,6 +100,9 @@ type Msg = GotSize Int | ChgSound String | ChgView Float Float -- Zoom, Center percent + | MoveStartPercent Int + | MoveEndPercent Int + | MoveView Int | ZoomPoint Float Float -- wheelDelta, xOffset | GotDrawn (Result D.Error String) | Select ( Float, Float ) @@ -97,51 +115,72 @@ update msg wave = case msg of GotSize size -> ( { wave | size = size - 2 * border } - , case wave.drawn of - SoundDrawn name -> - requestSoundDraw <| SoundView name wave.zoomFactor wave.startPercent - - _ -> - Cmd.none + , requestFullDraw True wave ) ChgSound name -> + if wave.drawn == SoundDrawn name then + ( wave, Cmd.none ) + + else + let + newWave = + { wave | drawn = Pending name, zoomFactor = init.zoomFactor, startPercent = init.startPercent } + in + ( newWave + , requestFullDraw False newWave + ) + + ChgView factor start -> let f = - init.zoomFactor + clamp 1 (1 / 0) factor a = - init.startPercent + clamp 0 (1 - 1 / f) start - chgRes = - ( { wave | drawn = Pending name, zoomFactor = f, startPercent = a } - , requestSoundDraw <| SoundView name f a - ) + newWave = + { wave | zoomFactor = f, startPercent = a } in - case wave.drawn of - SoundDrawn soundName -> - if name == soundName then - ( wave, Cmd.none ) + ( newWave, requestRedraw newWave ) - else - chgRes + MoveStartPercent d -> + let + viewPercent = + 1 / wave.zoomFactor - _ -> - chgRes + percentD = + mapPxToSoundPercent wave d - ChgView factor start -> - let f = - clamp 1 (1 / 0) factor + 1 / (viewPercent - percentD) - a = - clamp 0 (1 - 1 / f) start + newWave = + { wave + | startPercent = clamp 0 (1 - 1 / f) wave.startPercent + percentD + , zoomFactor = f + } + in + ( newWave, requestRedraw newWave ) + + MoveEndPercent d -> + let + viewPercent = + 1 / wave.zoomFactor + + percentD = + mapPxToSoundPercent wave d newWave = - { wave | zoomFactor = f, startPercent = a } + { wave + | zoomFactor = 1 / (viewPercent + percentD) + } in ( newWave, requestRedraw newWave ) + MoveView d -> + update (ChgView wave.zoomFactor (wave.startPercent + mapPxToSoundPercent wave d)) wave + ZoomPoint delta x -> let factor = @@ -167,7 +206,7 @@ update msg wave = ( { wave | drawn = SoundDrawn name }, Cmd.none ) else - ( wave, requestSoundDraw <| SoundView name wave.zoomFactor wave.startPercent ) + ( wave, requestFullDraw False wave ) _ -> ( wave, Cmd.none ) @@ -261,13 +300,16 @@ view : -> Element msg view wave mayCursors interState wrapInter wrapMsg = let + showMini = + mayCursors /= Nothing && wave.zoomFactor /= 1 + curs pos = let p = soundPercentToViewPercent wave pos in if p >= 0 && p <= 1 then - List.singleton << (cursor wave.height <| viewPercentToPx wave p) + List.singleton << (cursor waveHeight <| viewPercentToPx wave p) else always [] @@ -285,73 +327,140 @@ view wave mayCursors interState wrapInter wrapMsg = else [ selection ( a, b ) foo bar ] + + toMapPx = + soundPercentToMapPx wave + + miniCurs = + cursor mapHeight << toMapPx + + miniSel tup foo bar = + selection (Tuple.mapBoth toMapPx toMapPx tup) foo bar + + whiteSel = + rgba 0 0 0 0 + + greySel = + rgba 0.5 0.5 0.5 0.5 + + darkGreySel = + rgba 0.3 0.3 0.3 0.3 in - el - ((htmlAttribute <| - Attr.map wrapMsg <| - Events.on "wheel" <| - D.map3 (\deltaY clientX rect -> ZoomPoint deltaY <| clientX - rect.left - toFloat border) - (D.field "deltaY" D.float) - (D.field "clientX" D.float) - (DOM.currentTarget DOM.boundingClientRect) - ) - :: (case mayCursors of - Just cursors -> - (List.map (htmlAttribute << Attr.map wrapInter) <| Interact.dragSpaceEvents interState ZWave) - ++ [ Border.color <| rgb 0 0 0 - , Border.width border - , Bg.color <| rgb 1 1 1 - , alignBottom - ] - ++ (List.map (mapAttribute wrapInter) <| - case cursors of - Sound c -> - List.concat - [ sel ( 0, c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , sel ( c.end, 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , sel ( c.start, c.end ) (Just IWaveSel) <| rgba 0 0 0 0 - , curs c.start LoopStart - , curs c.end LoopEnd - , curs c.offset StartOffset - , case wave.sel of - Just points -> - sel points Nothing <| rgba 0.3 0.3 0.3 0.3 - - Nothing -> - [] - ] - - CollarDiv c -> - List.concat <| - [ sel ( 0, c.start ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , sel ( c.end, 1 ) Nothing <| rgba 0.5 0.5 0.5 0.5 - , sel ( c.start, c.end ) (Just IWaveSel) <| rgba 0 0 0 0 - , curs c.start LoopStart - , curs c.end LoopEnd - ] - ++ List.indexedMap (\i div -> curs div (Divide i)) c.divs - ) + column + [ Bg.color <| rgb 1 1 1 + , alignBottom + ] + [ el + (case ( showMini, mayCursors ) of + ( True, Just cursors ) -> + let + p1 = + wave.startPercent - Nothing -> - [] - ) - ) - <| - html <| - canvas - [ Attr.hidden <| mayCursors == Nothing - , Attr.id canvasId - , Attr.width wave.size - , Attr.height wave.height - ] - [] + p2 = + wave.startPercent + pxToSoundDist wave wave.size + in + List.map (mapAttribute wrapInter) <| + (List.map htmlAttribute <| Interact.dragSpaceEvents interState ZWaveMap) + ++ (case cursors of + Sound c -> + [ miniSel ( 0, c.start ) Nothing greySel + , miniSel ( c.end, 1 ) Nothing greySel + , miniSel ( p1, p2 ) (Just IWaveMapSel) whiteSel + , miniCurs c.start <| LoopStart Mini + , miniCurs c.end <| LoopEnd Mini + , miniCurs c.offset <| StartOffset Mini + ] + + CollarDiv c -> + [ miniSel ( 0, c.start ) Nothing greySel + , miniSel ( c.end, 1 ) Nothing greySel + , miniSel ( p1, p2 ) (Just IWaveMapSel) whiteSel + , miniCurs c.start <| LoopStart Mini + , miniCurs c.end <| LoopEnd Mini + ] + ++ List.indexedMap (\i div -> miniCurs div <| Divide i Mini) c.divs + ) + ++ [ miniCurs p1 ViewStart + , miniCurs p2 ViewEnd + ] + _ -> + [] + ) + <| + html <| + canvas + [ Attr.hidden <| not showMini + , Attr.id mapCanvasId + , Attr.width <| wave.size + 2 * border + , Attr.height mapHeight + ] + [] + , el + (case mayCursors of + Just cursors -> + ((htmlAttribute << Attr.map wrapMsg) <| + Events.on "wheel" <| + D.map3 (\deltaY clientX rect -> ZoomPoint deltaY <| clientX - rect.left - toFloat border) + (D.field "deltaY" D.float) + (D.field "clientX" D.float) + (DOM.currentTarget DOM.boundingClientRect) + ) + :: [ Border.color <| rgb 0 0 0 + , Border.width border + ] + ++ (List.map (mapAttribute wrapInter) <| + (List.map htmlAttribute <| Interact.dragSpaceEvents interState ZWave) + ++ (case cursors of + Sound c -> + List.concat + [ sel ( 0, c.start ) Nothing greySel + , sel ( c.end, 1 ) Nothing greySel + , sel ( c.start, c.end ) (Just IWaveSel) whiteSel + , curs c.start <| LoopStart Main + , curs c.end <| LoopEnd Main + , curs c.offset <| StartOffset Main + , case wave.sel of + Just points -> + sel points Nothing darkGreySel + + Nothing -> + [] + ] + + CollarDiv c -> + List.concat <| + [ sel ( 0, c.start ) Nothing greySel + , sel ( c.end, 1 ) Nothing greySel + , sel ( c.start, c.end ) (Just IWaveSel) whiteSel + , curs c.start <| LoopStart Main + , curs c.end <| LoopEnd Main + ] + ++ List.indexedMap (\i div -> curs div (Divide i Main)) c.divs + ) + ) -requestRedraw : Waveform -> Cmd msg -requestRedraw wave = + Nothing -> + [] + ) + <| + html <| + canvas + [ Attr.hidden <| mayCursors == Nothing + , Attr.id canvasId + , Attr.width wave.size + , Attr.height waveHeight + ] + [] + ] + + +requestHelper : Bool -> Bool -> { a | zoomFactor : Float, startPercent : Float, drawn : Drawing } -> Cmd msg +requestHelper full wait wave = let cmd soundName = - requestSoundDraw <| SoundView soundName wave.zoomFactor wave.startPercent + requestSoundDraw <| SoundRequest soundName wave.zoomFactor wave.startPercent wait full in case wave.drawn of SoundDrawn name -> @@ -364,11 +473,25 @@ requestRedraw wave = Cmd.none +requestRedraw : { a | zoomFactor : Float, startPercent : Float, drawn : Drawing } -> Cmd msg +requestRedraw = + requestHelper False False + + +requestFullDraw : Bool -> { a | zoomFactor : Float, startPercent : Float, drawn : Drawing } -> Cmd msg +requestFullDraw = + requestHelper True + + cursor : Int -> Int -> Cursor -> Attribute (Interact.Msg Interactable zone) cursor h pos cur = let size = - 8 + if h >= 100 then + 8 + + else + 6 handle attrs = inFront <| @@ -392,21 +515,24 @@ cursor h pos cur = , moveRight <| toFloat <| pos - border ] ++ (case cur of - LoopStart -> + LoopStart _ -> [ handle [ alignTop, moveUp <| toFloat border ] ] - LoopEnd -> + LoopEnd _ -> [ handle [ alignBottom, moveDown <| toFloat border ] ] - StartOffset -> + StartOffset _ -> [ handle [ centerY, moveDown <| toFloat h / 4 ] , handle [ centerY, moveUp <| toFloat h / 4 ] ] - Divide _ -> + Divide _ _ -> [ handle [ alignTop, moveUp <| toFloat border ] , handle [ alignBottom, moveDown <| toFloat border ] ] + + _ -> + [] ) ++ (List.map htmlAttribute <| Interact.draggableEvents <| IWaveCursor cur) ) @@ -482,3 +608,13 @@ soundDistToPx w = pxToSoundDist : Waveform -> Int -> Float pxToSoundDist w = pxToViewPercent w >> viewDistToSoundDist w + + +soundPercentToMapPx : Waveform -> Float -> Int +soundPercentToMapPx w p = + round <| p * (toFloat <| w.size + 2 * border) + + +mapPxToSoundPercent : Waveform -> Int -> Float +mapPxToSoundPercent w px = + toFloat px / toFloat (w.size + 2 * border) From 685bf46af126921e194a0ecfd9dc1757f1d45258 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sun, 28 Mar 2021 12:12:37 +0200 Subject: [PATCH 24/43] wave shortcuts first draft --- src/Doc.elm | 10 ++++++ src/Editor/Mobile.elm | 84 ++++++++++++++++++++++++------------------- src/Main.elm | 32 +++++++++-------- src/Waveform.elm | 43 ++++++++++++++++++++++ 4 files changed, 118 insertions(+), 51 deletions(-) diff --git a/src/Doc.elm b/src/Doc.elm index c58b790..7b64c3a 100644 --- a/src/Doc.elm +++ b/src/Doc.elm @@ -5,6 +5,7 @@ import Data.Common as Common exposing (Identifier) import Data.Content as Content exposing (Content) import Data.Mobile as Mobile exposing (Geer, Mobeel) import Data.Wheel as Wheel exposing (Conteet, Wheel) +import Dict exposing (Dict) import Editor.Interacting exposing (Interactable, Zone(..)) import Editor.Mobile as Editor import Element exposing (..) @@ -59,6 +60,7 @@ type Shortcut | Right | Suppr | Pack + | Editor Editor.Msg type Msg @@ -228,6 +230,9 @@ update msg doc = Right -> update (MobileMsg <| Editor.CursorRight) doc + Editor subMsg -> + update (MobileMsg subMsg) doc + DirectionRepeat dir -> update (MobileMsg <| Editor.SvgMsg <| PanSvg.Pan dir) doc @@ -294,6 +299,11 @@ keyCodeToMode = Editor.keyCodeToMode +keyCodeToShortcut : Model -> Dict String Shortcut +keyCodeToShortcut model = + Dict.map (always Editor) <| Editor.keyCodeToShortcut model.editor <| getViewing model + + view : Model -> Element Msg view doc = row [ height fill, width fill ] <| diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 175c681..497fda3 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -8,7 +8,7 @@ import Data.Content as Content exposing (Content) import Data.Gear as Gear import Data.Mobile as Mobile exposing (Geer, Mobeel) import Data.Wheel as Wheel exposing (Conteet, Wheel) -import Dict +import Dict exposing (Dict) import Editor.Interacting exposing (..) import Element exposing (..) import Element.Background as Bg @@ -130,6 +130,11 @@ keyCodeToMode = ] +keyCodeToShortcut : Model -> Mobeel -> Dict String Msg +keyCodeToShortcut mod mob = + Dict.map (always WaveMsg) <| Waveform.keyCodeToShortcut <| getWavePoints mod mob + + type alias LinkInfo = { link : Link Geer, fractInput : FractInput } @@ -1111,54 +1116,59 @@ viewExtraTools model = ) +getWavePoints : Model -> Mobeel -> Maybe Waveform.Cursors +getWavePoints model mobile = + case ( model.tool, model.edit ) of + ( Edit _, [ id ] ) -> + let + g = + Coll.get id mobile.gears --- TODO Split between mobile view, motor view, harmony view, and whatever else - - -viewContent : ( Model, Mobeel ) -> Element Msg -viewContent ( model, mobile ) = - let - mayWavePoints = - case ( model.tool, model.edit ) of - ( Edit _, [ id ] ) -> - let - g = - Coll.get id mobile.gears + ( start, end ) = + Wheel.getLoopPercents g + in + case ( g.wheel.viewContent, Wheel.getContent g ) of + ( True, Content.S s ) -> + if Waveform.isDrawn model.wave <| Sound.toString s then + Just <| Waveform.Sound { offset = g.wheel.startPercent, start = start, end = end } - ( start, end ) = - Wheel.getLoopPercents g - in - case ( g.wheel.viewContent, Wheel.getContent g ) of - ( True, Content.S s ) -> - if Waveform.isDrawn model.wave <| Sound.toString s then - Just <| Waveform.Sound { offset = g.wheel.startPercent, start = start, end = end } + else + Nothing + + ( True, Content.C c ) -> + case c.oneSound of + Just oneSound -> + if Waveform.isDrawn model.wave oneSound.soundName then + Just <| + Waveform.CollarDiv + { start = oneSound.start + , end = oneSound.end + , divs = oneSound.divs + } else Nothing - ( True, Content.C c ) -> - case c.oneSound of - Just oneSound -> - if Waveform.isDrawn model.wave oneSound.soundName then - Just <| - Waveform.CollarDiv - { start = oneSound.start - , end = oneSound.end - , divs = oneSound.divs - } - - else - Nothing - - _ -> - Nothing - _ -> Nothing _ -> Nothing + _ -> + Nothing + + + +-- TODO Split between mobile view, motor view, harmony view, and whatever else + + +viewContent : ( Model, Mobeel ) -> Element Msg +viewContent ( model, mobile ) = + let + mayWavePoints = + getWavePoints model mobile + getMod : Id Geer -> Wheel.Mod getMod id = if (model.tool == Edit False || model.tool == Edit True) && List.member id model.edit then diff --git a/src/Main.elm b/src/Main.elm index c241f11..fb72692 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -716,7 +716,7 @@ update msg model = Tuple.mapSecond (\cm -> Cmd.batch [ cm, c ]) <| update (ChangedMode NoMode) m Keys.Press code -> - case Dict.get code keyCodeToShortcut of + case Dict.get code <| keyCodeToShortcut model of Just press -> let ( doc, cmd ) = @@ -778,19 +778,23 @@ keyCodeToMode = ++ List.map (Tuple.mapSecond EditorMode) Doc.keyCodeToMode -keyCodeToShortcut : Dict String Doc.Shortcut -keyCodeToShortcut = - Dict.fromList - [ ( "KeyZ", Doc.Tool 1 ) - , ( "KeyX", Doc.Tool 2 ) - , ( "KeyC", Doc.Tool 3 ) - , ( "Space", Doc.Play ) - , ( "ArrowLeft", Doc.Left ) - , ( "ArrowRight", Doc.Right ) - , ( "Backspace", Doc.Suppr ) - , ( "Delete", Doc.Suppr ) - , ( "KeyT", Doc.Pack ) - ] +keyCodeToShortcut : Model -> Dict String Doc.Shortcut +keyCodeToShortcut model = + Dict.union + (Dict.fromList + [ ( "KeyZ", Doc.Tool 1 ) + , ( "KeyX", Doc.Tool 2 ) + , ( "KeyC", Doc.Tool 3 ) + , ( "Space", Doc.Play ) + , ( "ArrowLeft", Doc.Left ) + , ( "ArrowRight", Doc.Right ) + , ( "Backspace", Doc.Suppr ) + , ( "Delete", Doc.Suppr ) + , ( "KeyT", Doc.Pack ) + ] + ) + <| + Doc.keyCodeToShortcut model.doc keyCodeToDirection : Dict String PanSvg.Direction diff --git a/src/Waveform.elm b/src/Waveform.elm index f9056d8..cc3d823 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -1,6 +1,7 @@ port module Waveform exposing (..) import DOM +import Dict exposing (Dict) import Editor.Interacting exposing (..) import Element exposing (..) import Element.Background as Bg @@ -96,6 +97,12 @@ isDrawn { drawn } name = False +type Mark + = Cursor Cursor + | Start + | End + + type Msg = GotSize Int | ChgSound String @@ -103,7 +110,9 @@ type Msg | MoveStartPercent Int | MoveEndPercent Int | MoveView Int + | CenterOn Mark (Maybe Cursors) | ZoomPoint Float Float -- wheelDelta, xOffset + | Zoom Bool | GotDrawn (Result D.Error String) | Select ( Float, Float ) | MoveSel Float @@ -181,6 +190,9 @@ update msg wave = MoveView d -> update (ChgView wave.zoomFactor (wave.startPercent + mapPxToSoundPercent wave d)) wave + CenterOn cur mayCursors -> + Debug.todo "center on cursors" + ZoomPoint delta x -> let factor = @@ -197,6 +209,26 @@ update msg wave = in update (ChgView f a) wave + Zoom b -> + let + factor = + if b then + 0.9 + + else + 1.1 + + f = + clamp 1 (1 / 0) <| wave.zoomFactor / factor + + a = + clamp 0 (1 - 1 / f) <| wave.startPercent - (1 / f - 1 / wave.zoomFactor) / 2 + + newWave = + { wave | zoomFactor = f, startPercent = a } + in + ( newWave, requestRedraw newWave ) + GotDrawn res -> case res of Ok soundName -> @@ -286,6 +318,17 @@ sub = soundDrawn (GotDrawn << D.decodeValue D.string) +keyCodeToShortcut : Maybe Cursors -> Dict String Msg +keyCodeToShortcut mayC = + Dict.fromList + [ ( "KeyG", Zoom False ) + , ( "KeyH", Zoom True ) + , ( "KeyB", MoveView -20 ) + , ( "KeyN", MoveView 20 ) + , ( "Key1", CenterOn Start mayC ) + ] + + type Cursors = Sound { offset : Float, start : Float, end : Float } | CollarDiv { start : Float, end : Float, divs : List Float } From e55a6fef7d1be09440e42e8acc3ac996756552aa Mon Sep 17 00:00:00 2001 From: cbossut Date: Sun, 28 Mar 2021 19:59:37 +0200 Subject: [PATCH 25/43] clean fix mic sync rec --- ports.js | 25 +++---------------------- scheduler.js | 2 +- src/Main.elm | 48 +++++++++++++----------------------------------- 3 files changed, 17 insertions(+), 58 deletions(-) diff --git a/ports.js b/ports.js index 6378253..65a232e 100644 --- a/ports.js +++ b/ports.js @@ -77,7 +77,6 @@ let mic , micToRecord , micRecorder , recording -//const micLatency = 0.180 // calc with https://superpowered.com/webbrowserlatency function openMic() { navigator.mediaDevices.getUserMedia({audio : true}).then(stream => { mic = stream @@ -89,34 +88,16 @@ function openMic() { function inputRec(args) { let name = args[0] - , micLatency = args[1] + , start = args[1] if (name) { micRecorder.stop() - console.log(micLatency/1000) -// micRecorder.exportWAV(bl => app.ports.gotNewSample.send(new File([bl], name + ".wav", {type: "audio/wav"}))) - micRecorder.getBuffer(bs => { - let start = Math.round(ctx.sampleRate * micLatency / 1000) - , length = bs[0].length - start - , newBuf = new AudioBuffer( - { length : length - , numberOfChannels : bs.length - , sampleRate : ctx.sampleRate - } - ) - - for (let i = 0 ; i < bs.length ; i++) { - let chan = bs[i].slice(start) - newBuf.copyToChannel(chan, i) - } - - app.ports.gotNewSample.send(new File([audioBufferToWav(newBuf)], name + ".wav", {type: "audio/wav"})) - }) + micRecorder.exportWAV(bl => app.ports.gotNewSample.send(new File([bl], name + ".wav", {type: "audio/wav"}))) micRecorder.clear() recording = false if (!scheduler.running) ctx.suspend() } else { if (mic) { -// ctx.resume() + if (start) ctx.resume() micRecorder.record() recording = true } else console.error("won’t record mic if it ain’t opened !") diff --git a/scheduler.js b/scheduler.js index 4779255..12351bc 100644 --- a/scheduler.js +++ b/scheduler.js @@ -63,7 +63,7 @@ let scheduler = { model.view.moveTo(0) } - if (!recording) ctx.suspend() + ctx.suspend() this.intervalId = -1 this.nextRequestId = -1 diff --git a/src/Main.elm b/src/Main.elm index fb72692..08d7f5f 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -47,7 +47,7 @@ port openMic : () -> Cmd msg port micOpened : (() -> msg) -> Sub msg -port inputRec : ( String, Int ) -> Cmd msg +port inputRec : ( String, Bool ) -> Cmd msg port gotNewSample : (D.Value -> msg) -> Sub msg @@ -80,7 +80,7 @@ main = type alias Model = { connected : Bool , currentUrl : Url.Url - , micState : Maybe ( Bool, ( String, Int ) ) -- Recording, FileName, Latency in ms + , micState : Maybe ( Bool, String ) -- Recording, FileName , soundList : Dict String SoundListType , loadedSoundList : List Sound , showDirLoad : Bool @@ -155,9 +155,8 @@ type Msg | RequestOpenMic | MicOpened | StartMicRec - | EndMicRec String Int + | EndMicRec String | EnteredNewRecName String - | EnteredMicLatency String | ClickedUploadSound | UploadSounds File (List File) | GotNewSample (Result D.Error File) @@ -511,35 +510,20 @@ update msg model = ( model, openMic () ) MicOpened -> - ( { model | micState = Just ( False, ( "", 0 ) ) }, Cmd.none ) + ( { model | micState = Just ( False, "" ) }, Cmd.none ) StartMicRec -> ( { model | micState = Maybe.map (Tuple.mapFirst <| always True) model.micState } - , inputRec ( "", 0 ) + , inputRec ( "", Coll.isEmpty (Doc.getViewing model.doc).gears ) ) - EndMicRec fileName latency -> + EndMicRec fileName -> ( { model | micState = Maybe.map (Tuple.mapFirst <| always False) model.micState } - , inputRec ( fileName, latency ) + , inputRec ( fileName, True ) ) EnteredNewRecName fileName -> - ( { model | micState = Maybe.map (Tuple.mapSecond <| Tuple.mapFirst <| always fileName) model.micState } - , Cmd.none - ) - - EnteredMicLatency lat -> - ( { model - | micState = - Maybe.map - (Tuple.mapSecond <| - Tuple.mapSecond <| - always <| - Maybe.withDefault 0 <| - String.toInt lat - ) - model.micState - } + ( { model | micState = Maybe.map (Tuple.mapSecond <| always fileName) model.micState } , Cmd.none ) @@ -687,8 +671,8 @@ update msg model = ( _, Doc.MobileMsg (Editor.ChangedMode (Editor.ChangeSound _)), _ ) -> ( { model | fileExplorerTab = LoadedSounds }, Cmd.none ) - ( Just ( True, ( name, latency ) ), Doc.MobileMsg Editor.ToggleEngine, Editor.Play True _ ) -> - update (EndMicRec name latency) model + ( Just ( True, name ), Doc.MobileMsg Editor.ToggleEngine, Editor.Play True _ ) -> + update (EndMicRec name) model _ -> ( model, Cmd.none ) @@ -912,7 +896,7 @@ viewSounds model = viewOpenRefreshButtons ClickedUploadSound RequestSoundList model.connected , column [ width fill, spacing 20 ] <| case model.micState of - Just ( False, ( name, latency ) ) -> + Just ( False, name ) -> [ row [] [ Input.button [] { onPress = @@ -923,12 +907,6 @@ viewSounds model = Just StartMicRec , label = text "Rec Mic" } - , Input.text [ Font.color (rgb 0 0 0), paddingXY 5 0 ] - { text = String.fromInt latency - , placeholder = Nothing - , label = Input.labelHidden "Mic latency in ms" - , onChange = EnteredMicLatency - } ] , Input.text [ Font.color (rgb 0 0 0), paddingXY 5 0 ] { text = name @@ -938,8 +916,8 @@ viewSounds model = } ] - Just ( True, ( name, latency ) ) -> - [ Input.button [] { onPress = Just <| EndMicRec name latency, label = text "Stop Mic" } + Just ( True, name ) -> + [ Input.button [] { onPress = Just <| EndMicRec name, label = text "Stop Mic" } , text name ] From 24073721dc2f300f9dc9c19efc91af2b256672a3 Mon Sep 17 00:00:00 2001 From: cbossut Date: Mon, 29 Mar 2021 00:19:43 +0200 Subject: [PATCH 26/43] stop rec when shortcut space for stop mobile --- src/Main.elm | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Main.elm b/src/Main.elm index 08d7f5f..7edb2c2 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -703,10 +703,18 @@ update msg model = case Dict.get code <| keyCodeToShortcut model of Just press -> let - ( doc, cmd ) = - Doc.update (Doc.KeyPressed press) m.doc + ( newModel, cmd ) = + case ( model.micState, press, model.doc.editor.tool ) of + ( Just ( True, name ), Doc.Play, Editor.Play True _ ) -> + update (EndMicRec name) model + + _ -> + ( m, Cmd.none ) + + ( doc, docCmd ) = + Doc.update (Doc.KeyPressed press) newModel.doc in - ( { m | doc = doc }, Cmd.batch [ c, Cmd.map DocMsg cmd ] ) + ( { newModel | doc = doc }, Cmd.batch [ c, Cmd.map DocMsg docCmd, cmd ] ) Nothing -> ( m, c ) From a2108c239ebd0572e71253b070845f0fa73357d5 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sun, 4 Apr 2021 21:36:00 +0200 Subject: [PATCH 27/43] uncollar multiple beads --- src/Data/Collar.elm | 40 ++++++++----- src/Editor/Mobile.elm | 128 ++++++++++++++++++++++++++++++------------ 2 files changed, 117 insertions(+), 51 deletions(-) diff --git a/src/Data/Collar.elm b/src/Data/Collar.elm index 9341188..5152a92 100644 --- a/src/Data/Collar.elm +++ b/src/Data/Collar.elm @@ -128,22 +128,32 @@ get = Content.getBead -add : Int -> Beed -> Colleer -> Colleer -add i b c = - if i <= 0 then - { c - | head = b - , beads = c.head :: c.beads - , matrice = c.matrice + 1 - , oneSound = Nothing - } +addBeads : Int -> List Beed -> Colleer -> Colleer +addBeads i bs c = + case bs of + [] -> + c + + head :: tail -> + if i <= 0 then + { c + | head = head + , beads = List.concat [ tail, c.head :: c.beads ] + , matrice = c.matrice + List.length bs + , oneSound = Nothing + } - else - { c - | beads = List.concat [ List.take (i - 1) c.beads, [ b ], List.drop (i - 1) c.beads ] - , matrice = c.matrice + 1 - , oneSound = Nothing - } + else + { c + | beads = List.concat [ List.take (i - 1) c.beads, bs, List.drop (i - 1) c.beads ] + , matrice = c.matrice + List.length bs + , oneSound = Nothing + } + + +add : Int -> Beed -> Colleer -> Colleer +add i b = + addBeads i [ b ] rm : Int -> Colleer -> Colleer diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 1ef3a0c..d9eb244 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -483,7 +483,8 @@ update msg ( model, mobile ) = _ -> Maybe.withDefault tmp <| - uncollar ( id, List.take (List.length l - 1) l ) tmp + Maybe.map Tuple.first <| + uncollar ( id, List.take (List.length l - 1) l ) tmp in { return | model = @@ -799,7 +800,15 @@ update msg ( model, mobile ) = uncollar ( id, [] ) mobile in Maybe.withDefault return <| - Maybe.map (\m -> { return | mobile = m, toUndo = Do }) mayRes + Maybe.map + (\( m, ( newSel, _ ) ) -> + { return + | mobile = m + , toUndo = Do + , model = { model | edit = [ newSel ] } + } + ) + mayRes EnteredCollarMult str -> case toIntOrEmpty str of @@ -1670,18 +1679,6 @@ viewEditDetails model mobile = } in case Wheel.getContent g of - Content.C col -> - if List.length col.beads == 0 then - Input.button [] - { label = text "Décollier" - , onPress = Just <| UnCollar id - } - - else - row [ spacing 16 ] <| - simpleBtn - :: multBtns - Content.S s -> row [ spacing 16 ] <| simpleBtn @@ -1692,6 +1689,10 @@ viewEditDetails model mobile = row [ spacing 16 ] <| simpleBtn :: multBtns + , Input.button [] + { label = text "Décollier" + , onPress = Just <| UnCollar id + } , if id == mobile.motor then Input.button [] { onPress = Just <| ChangedMode SelectMotor @@ -2061,7 +2062,7 @@ addBead model mobile bead = Nothing -uncollar : Identifier -> Mobeel -> Maybe Mobeel +uncollar : Identifier -> Mobeel -> Maybe ( Mobeel, Identifier ) uncollar id m = let w = @@ -2069,28 +2070,83 @@ uncollar id m = in case Wheel.getWheelContent w of Content.C col -> - if Collar.length col == 1 then - let - newMob = - CommonData.updateWheel id (Wheel.ChangeContent <| Wheel.getContent col.head) m - - gId = - Tuple.first id - - getContentLength = - CommonData.getWheeledContentLength << Coll.get gId << .gears - in - Just <| - { newMob - | gears = - Harmo.changeContentKeepLength gId - (getContentLength newMob) - (getContentLength m) - newMob.gears - } + let + beads = + Collar.getBeads col - else - Nothing + contentLength = + Collar.getTotalLength col + + ( gId, listPos ) = + id + in + case List.reverse listPos of + [] -> + let + nextId = + Tuple.first <| Coll.insertTellId (Gear.default Wheel.default) m.gears + + newMotor = + if m.motor == gId then + nextId + + else + m.motor + + newMob = + CommonData.deleteWheel id { m | motor = newMotor } Mobile.rm Collar.rm + + ( gearPos, gearLength ) = + Mobile.gearPosSize gId m.gears + + xMin = + Vec.getX gearPos - gearLength / 2 + + ratio = + gearLength / contentLength + + newGears = + Tuple.first <| + List.foldl + (\{ length, wheel } ( gears, x ) -> + let + realLength = + length * ratio + in + ( gears + |> Coll.insert + { motor = Motor.default + , wheel = wheel + , harmony = Harmo.newRate <| realLength / CommonData.getWheeledContentLength { wheel = wheel } + , pos = Vec.setX (x + realLength / 2) gearPos + } + , x + realLength + ) + ) + ( newMob.gears, xMin ) + beads + in + Just ( { newMob | gears = newGears }, ( nextId, [] ) ) + + lastIndex :: revRest -> + let + upColId = + ( gId, List.reverse revRest ) + in + case Wheel.getWheelContent <| CommonData.getWheel upColId m of + Content.C upCol -> + Just + ( CommonData.updateWheel upColId + (Wheel.ChangeContent <| + Content.C <| + Collar.addBeads lastIndex beads upCol + ) + m + , id + ) + + _ -> + Nothing _ -> Nothing From 22c5aa07f2c4d21c832ff3c6064979a588f7df49 Mon Sep 17 00:00:00 2001 From: cbossut Date: Fri, 23 Apr 2021 10:51:02 +0200 Subject: [PATCH 28/43] Suppr is only mode, no more shortcut --- src/Main.elm | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Main.elm b/src/Main.elm index 2014be4..4c078a7 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -765,8 +765,6 @@ keyCodeToShortcut = , ( "Space", Doc.Play ) , ( "ArrowLeft", Doc.Left ) , ( "ArrowRight", Doc.Right ) - , ( "Backspace", Doc.Suppr ) - , ( "Delete", Doc.Suppr ) , ( "KeyT", Doc.Pack ) ] From d156ed6fc3e65cc375d938b484e7578e46fcbce3 Mon Sep 17 00:00:00 2001 From: cbossut Date: Fri, 23 Apr 2021 16:08:09 +0200 Subject: [PATCH 29/43] Clone mode with Q key --- src/Data/Mobile.elm | 10 +++++++--- src/Editor/Mobile.elm | 19 +++++++++++++++---- src/Harmony.elm | 9 +++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/Data/Mobile.elm b/src/Data/Mobile.elm index 080d0f8..a7647bf 100644 --- a/src/Data/Mobile.elm +++ b/src/Data/Mobile.elm @@ -60,8 +60,8 @@ newSizedGear p l w = { pos = p, harmony = Harmo.newRate (l / getWheeledContentLength { wheel = w }), motor = [], wheel = w } -copy : Vec2 -> Id Geer -> Coll Geer -> Coll Geer -copy move id coll = +copy : Bool -> Vec2 -> Id Geer -> Coll Geer -> Coll Geer +copy harmo move id coll = let g = Coll.get id coll @@ -75,7 +75,11 @@ copy move id coll = ( newId, newColl ) = Coll.insertTellId newG coll in - Harmo.makeCopy id newId newColl + if harmo then + Harmo.makeCopy id newId newColl + + else + Harmo.toRate getWheeledContentLength newId newColl toDrawLink : Coll Geer -> Link Geer -> DrawLink diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index d9eb244..d0602e8 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -117,6 +117,7 @@ type Mode | SelectMotor | Alternate | Solo + | Clone keyCodeToMode : List ( String, Mode ) @@ -127,6 +128,7 @@ keyCodeToMode = , ( "Backspace", SupprMode ) , ( "KeyQ", Alternate ) , ( "KeyS", Solo ) + , ( "KeyA", Clone ) ] @@ -211,7 +213,7 @@ type Msg | NewBead Conteet | UnpackBead ( Wheel, Float ) Bool -- - | CopyGear (Id Geer) + | CopyGear Bool (Id Geer) | CopyContent Wheel | NewGear Vec2 Conteet | DeleteWheel Identifier @@ -441,12 +443,12 @@ update msg ( model, mobile ) = _ -> return -} - CopyGear id -> + CopyGear harmo id -> let d = vec2 (Mobile.getLengthId id mobile.gears * 1.1) 0 in - { return | mobile = { mobile | gears = Mobile.copy d id mobile.gears }, toUndo = Do } + { return | mobile = { mobile | gears = Mobile.copy harmo d id mobile.gears }, toUndo = Do } CopyContent w -> case model.edit of @@ -2266,6 +2268,15 @@ manageInteractEvent event model mobile = _ -> return + Clone -> + -- TODO hover show pos of new wheel, which is linked to mouse pos to center of wheel + case ( event.item, event.action ) of + ( IWheel ( id, [] ), Interact.Clicked _ ) -> + update (CopyGear False id) ( model, mobile ) + + _ -> + return + Alternate -> case model.tool of Play _ _ -> @@ -2567,7 +2578,7 @@ interactHarmonize event model mobile = case ( event.item, event.action, model.dragging ) of -- COPY ( IWheel ( id, [] ), Interact.Clicked _, _ ) -> - update (CopyGear id) ( model, mobile ) + update (CopyGear True id) ( model, mobile ) -- RESIZE ( IResizeHandle id add, Interact.Dragged { startD } _ _, NoDrag ) -> diff --git a/src/Harmony.elm b/src/Harmony.elm index 5e0f01e..879d87d 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -133,6 +133,15 @@ changeRate id newDur contentLength = changeSelfUnit id <| Rate (newDur / contentLength) +toRate : (Harmonized g -> Float) -> Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g) +toRate getContentLength id coll = + let + g = + Coll.get id coll + in + changeSelfUnit id (Rate (getLength getContentLength g coll / getContentLength g)) coll + + changeDuration : Id (Harmonized g) -> Float -> Coll (Harmonized g) -> Coll (Harmonized g) changeDuration id newDur = changeSelfUnit id <| Duration newDur From db1e2cb004503fb61d506c6bed631ed105730dd1 Mon Sep 17 00:00:00 2001 From: cbossut Date: Fri, 23 Apr 2021 16:45:42 +0200 Subject: [PATCH 30/43] Fix toContentLength losing harmo group and links --- src/Harmony.elm | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Harmony.elm b/src/Harmony.elm index 879d87d..c13fac5 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -168,15 +168,8 @@ resizeFree id length contentLength coll = toContentLength : Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g) -toContentLength id coll = - case (getHarmo id coll).ref of - Self _ -> - Coll.update id (\g -> { g | harmony = newRate 1 }) coll - - Other rId -> - coll - |> Coll.update id (\g -> { g | harmony = newRate 1 }) - |> Coll.update (Coll.idMap rId) (remove id) +toContentLength id = + changeSelfUnit id <| Rate 1 makeCopy : Id (Harmonized g) -> Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g) From 36485a9048bb3773656f934f4df57716447e9ef8 Mon Sep 17 00:00:00 2001 From: cbossut Date: Fri, 23 Apr 2021 17:49:49 +0200 Subject: [PATCH 31/43] Fix name and path confusion Rename where necessary Clean Keep oneSound collars with soundName --- ports.js | 42 +++++++++++++++++++++--------------------- scheduler.js | 14 +++++++------- src/Data/Collar.elm | 2 +- src/Data/Common.elm | 42 +++++++++++++++++++++++------------------- src/Data/Content.elm | 6 +++--- src/Data/Mobile.elm | 17 ----------------- src/Data/Wheel.elm | 42 ++++++++++++++++-------------------------- src/Editor/Mobile.elm | 26 +++++++++++++++----------- src/Engine.elm | 2 +- src/Main.elm | 10 +++++----- src/Sound.elm | 15 ++------------- 11 files changed, 94 insertions(+), 124 deletions(-) diff --git a/ports.js b/ports.js index bec75bd..a9af671 100644 --- a/ports.js +++ b/ports.js @@ -19,41 +19,41 @@ function sendSize(entries) { app.ports.newSVGSize.send(entries[0].contentRect) } -function drawSound(soundName) { - if (buffers[soundName]) { - drawSamples(Array.from(buffers[soundName].getChannelData(0))) // TODO mix channels ? - app.ports.soundDrawn.send(soundName) - } else console.log(soundName + ' isn’t loaded, cannot draw') +function drawSound(soundPath) { + if (buffers[soundPath]) { + drawSamples(Array.from(buffers[soundPath].getChannelData(0))) // TODO mix channels ? + app.ports.soundDrawn.send(soundPath) + } else console.log(soundPath + ' isn’t loaded, cannot draw') } -function loadSound(soundName) { - if (buffers[soundName]) { - app.ports.soundLoaded.send(soundName + ' already Loaded') +function loadSound(soundPath) { + if (buffers[soundPath]) { + app.ports.soundLoaded.send(soundPath + ' already Loaded') } else { - createBuffer(soundName).then(b => { - buffers[soundName] = b - loadOk(soundName) - }).catch(err => loadErr(err, soundName)) + createBuffer(soundPath).then(b => { + buffers[soundPath] = b + loadOk(soundPath) + }).catch(err => loadErr(err, soundPath)) } } -async function createBuffer(soundName) { - const response = await fetch('./sons/' + soundName) +async function createBuffer(soundPath) { + const response = await fetch('./sons/' + soundPath) , arrayBuffer = await response.arrayBuffer() , audioBuffer = await ctx.decodeAudioData(arrayBuffer) return audioBuffer } -function loadOk(soundName) { +function loadOk(soundPath) { app.ports.soundLoaded.send( - { path : soundName - , length : buffers[soundName].duration + { path : soundPath + , length : buffers[soundPath].duration }) } -function loadErr(err, soundName) { +function loadErr(err, soundPath) { console.error(err) - app.ports.soundLoaded.send(soundName + ' got ' + err) + app.ports.soundLoaded.send(soundPath + ' got ' + err) } function toggleRecord(bool) { @@ -95,9 +95,9 @@ function inputRec(name) { } function cutSample(infos) { - if (!buffers[infos.fromFileName]) {console.error(infos.fromFileName + " ain’t loaded, cannot cut");return;} + if (!buffers[infos.fromSoundPath]) {console.error(infos.fromFileName + " ain’t loaded, cannot cut");return;} - let buf = buffers[infos.fromFileName] + let buf = buffers[infos.fromSoundPath] // TODO maybe round ? , start = infos.percents[0] * buf.length - 1 , end = infos.percents[1] * buf.length + 1 diff --git a/scheduler.js b/scheduler.js index 4779255..6154e26 100644 --- a/scheduler.js +++ b/scheduler.js @@ -48,7 +48,7 @@ let scheduler = { this.running = false let stopWheel = model => { - if (model.soundName) { + if (model.soundPath) { model.players.forEach(pl => pl.node.stop()) } if (model.collar || model.mobile) { @@ -97,7 +97,7 @@ let scheduler = { } // TODO volume should rather be in dB model.updateVolume() - if (model.soundName) { + if (model.soundPath) { // WARNING in model, startPercent is of whole sound, here it’s of content model.startPercent = (model.startPercent - model.loopPercents[0]) / (model.loopPercents[1] - model.loopPercents[0]) @@ -108,7 +108,7 @@ let scheduler = { , scheduler.lookAhead ) } - model.buffer = buffers[model.soundName] + model.buffer = buffers[model.soundPath] // TODO beware, buffer duration could differ from saved duration in Elm model (due to resampling) // probably it’s preferable to use saved duration from elm // but, is it compensated by downward TODO ? (in schedulePlayer) @@ -229,7 +229,7 @@ let scheduler = { t = nextState.date // Bring back the time and undo if (t <= now) console.error("undoing the past, now : " + now + " scheduler : " + t) - if (model.soundName) { + if (model.soundPath) { for (let pl of model.players) { if (pl.startTime <= t && t <= pl.stopTime) { pl.node.stop(this.toCtxTime(t)) @@ -277,7 +277,7 @@ let scheduler = { } else { // Normal pause - if (model.soundName) { + if (model.soundPath) { if (nextState.date <= t) { // No need to play more, even partially nextState.percent = clampPercent(0 - model.startPercent) @@ -335,7 +335,7 @@ let scheduler = { } else { // And keep playing - if (model.soundName) { + if (model.soundPath) { let newPlayers = this.scheduleLoop(t, max, model) model.players = model.players.concat(newPlayers) t = model.players[model.players.length - 1].stopTime @@ -363,7 +363,7 @@ let scheduler = { let contentPercent = clampPercent(lastState.percent + model.startPercent) - if (model.soundName) { + if (model.soundPath) { let offsetDur = contentPercent * model.duration + model.loopStartDur , newPlayer = this.scheduleStart(t, model, offsetDur) model.players.push(newPlayer) diff --git a/src/Data/Collar.elm b/src/Data/Collar.elm index 5152a92..30e651b 100644 --- a/src/Data/Collar.elm +++ b/src/Data/Collar.elm @@ -82,7 +82,7 @@ fromSoundDiv s d l = , beads = rest , oneSound = Just - { soundName = Sound.toString s + { path = Sound.getPath s , start = Tuple.first loopPercents , end = Tuple.second loopPercents , divs = divs diff --git a/src/Data/Common.elm b/src/Data/Common.elm index 6e41e23..8614d0a 100644 --- a/src/Data/Common.elm +++ b/src/Data/Common.elm @@ -17,31 +17,35 @@ type alias Identifier = getName : Identifier -> Mobile Wheel -> String -getName ( id, l ) mobile = +getName id mobile = let w = - getWheel ( id, l ) mobile + getWheel id mobile + + fileNameFromPath = + Maybe.withDefault "" + << List.head + << String.split "." + << Maybe.withDefault "" + << List.head + << List.reverse + << String.split "/" in if String.isEmpty w.name then - case l of - [] -> - case Wheel.getWheelContent w of - Content.S s -> - let - fileName = - Sound.fileName s - in - if String.isEmpty fileName then - Gear.toUID id - - else - fileName - - _ -> - Gear.toUID id + case Wheel.getWheelContent w of + Content.S s -> + fileNameFromPath <| Sound.getPath s + + Content.C c -> + case c.oneSound of + Just { path } -> + fileNameFromPath path + + Nothing -> + toUid id _ -> - toUid ( id, l ) + toUid id else w.name diff --git a/src/Data/Content.elm b/src/Data/Content.elm index 60fe99a..513e0ce 100644 --- a/src/Data/Content.elm +++ b/src/Data/Content.elm @@ -106,7 +106,7 @@ type alias Collar item = , beads : List (Bead item) -- WARNING second source of truth, just a shortcut to sounds internals - , oneSound : Maybe { soundName : String, start : Float, end : Float, divs : List Float } + , oneSound : Maybe { path : String, start : Float, end : Float, divs : List Float } } @@ -402,7 +402,7 @@ collarEncoder wheelEncoder c = ++ (Maybe.withDefault [] <| Maybe.map (\oneSound -> - [ ( "oneSoundName", E.string oneSound.soundName ) + [ ( "oneSoundName", E.string oneSound.path ) , ( "divs", E.list E.float oneSound.divs ) , ( "start", E.float oneSound.start ) , ( "end", E.float oneSound.end ) @@ -442,7 +442,7 @@ collarDecoder wheelDecoder = , oneSound = Maybe.map4 (\str start end divs -> - { soundName = str + { path = str , start = start , end = end , divs = divs diff --git a/src/Data/Mobile.elm b/src/Data/Mobile.elm index a7647bf..09911bd 100644 --- a/src/Data/Mobile.elm +++ b/src/Data/Mobile.elm @@ -97,23 +97,6 @@ toDrawLink coll l = Tuple.mapBoth f f l - --- TODO remove and use Common.getName instead - - -gearName : Id Geer -> Coll Geer -> String -gearName id coll = - let - name = - (Coll.get id coll).wheel.name - in - if String.isEmpty name then - Gear.toUID id - - else - name - - getLengthId : Id Geer -> Coll Geer -> Float getLengthId = Harmo.getLengthId getWheeledContentLength diff --git a/src/Data/Wheel.elm b/src/Data/Wheel.elm index 5af3a5b..8c39a8c 100644 --- a/src/Data/Wheel.elm +++ b/src/Data/Wheel.elm @@ -108,7 +108,7 @@ type alias Style = , motor : Bool , dashed : Bool , baseColor : Maybe Float - , named : Bool + , named : Maybe String } @@ -118,7 +118,7 @@ defaultStyle = , motor = False , dashed = False , baseColor = Nothing - , named = True + , named = Nothing } @@ -309,31 +309,21 @@ view w pos lengthTmp style mayWheelInter mayHandleInter uid = mayWheelInter in S.g [ SA.transform [ Translate (getX pos) (getY pos) ] ] <| - (if style.named then - [ S.text_ - [ SA.x <| Num 0 - , SA.y <| Num -(length * 3 / 4) - , SA.fontSize <| Num (length / 2) - , SA.textAnchor AnchorMiddle - , SA.stroke Color.white - , SA.strokeWidth <| Num (tickW / 4) + (case style.named of + Just name -> + [ S.text_ + [ SA.x <| Num 0 + , SA.y <| Num -(length * 3 / 4) + , SA.fontSize <| Num (length / 2) + , SA.textAnchor AnchorMiddle + , SA.stroke Color.white + , SA.strokeWidth <| Num (tickW / 4) + ] + [ text name ] ] - [ text <| - if String.isEmpty w.name then - case getWheelContent w of - Content.S s -> - Sound.fileName s - _ -> - "" - - else - w.name - ] - ] - - else - [ S.text_ [] [] ] + Nothing -> + [ S.text_ [] [] ] -- Because rotating g cannot be Keyed in TypedSvg, trick to prevent recreation ) ++ [ S.g hoverAttrs <| @@ -536,7 +526,7 @@ insideCollarView collar mayWheelInter parentUid = ( view b.wheel (vec2 (p + b.length / 2) 0) b.length - { defaultStyle | named = False } + { defaultStyle | named = Nothing } (Maybe.map (\( inter, l ) -> ( inter, l ++ [ i ] )) mayWheelInter) Nothing (Content.beadUIDExtension parentUid i) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index d0602e8..04afc72 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -47,7 +47,7 @@ port toggleRecord : Bool -> Cmd msg port gotRecord : (D.Value -> msg) -> Sub msg -port requestCutSample : { fromFileName : String, newFileName : String, percents : ( Float, Float ) } -> Cmd msg +port requestCutSample : { fromSoundPath : String, newFileName : String, percents : ( Float, Float ) } -> Cmd msg @@ -864,7 +864,7 @@ update msg ( model, mobile ) = in case Wheel.getContent g of Content.S s -> - { return | cmd = requestCutSample { fromFileName = Sound.toString s, newFileName = model.newSampleName, percents = Wheel.getLoopPercents g } } + { return | cmd = requestCutSample { fromSoundPath = Sound.getPath s, newFileName = model.newSampleName, percents = Wheel.getLoopPercents g } } _ -> return @@ -1127,7 +1127,7 @@ viewContent ( model, mobile ) = in case ( g.wheel.viewContent, Wheel.getContent g ) of ( True, Content.S s ) -> - if model.wave.drawn == (Waveform.SoundDrawn <| Sound.toString s) then + if model.wave.drawn == (Waveform.SoundDrawn <| Sound.getPath s) then Just <| Waveform.Sound { offset = g.wheel.startPercent, start = start, end = end } else @@ -1136,7 +1136,7 @@ viewContent ( model, mobile ) = ( True, Content.C c ) -> case c.oneSound of Just oneSound -> - if model.wave.drawn == Waveform.SoundDrawn oneSound.soundName then + if model.wave.drawn == Waveform.SoundDrawn oneSound.path then Just <| Waveform.CollarDiv { start = oneSound.start @@ -1279,10 +1279,14 @@ viewContent ( model, mobile ) = , named = case Interact.getInteract model.interact of Just ( IWheel idd, _ ) -> - id == Tuple.first idd + if id == Tuple.first idd then + Just <| CommonData.getName ( id, [] ) mobile + + else + Nothing _ -> - False + Nothing } (Just ( IWheel << Tuple.pair id, [] )) (Just <| IResizeHandle id) @@ -1562,10 +1566,10 @@ viewEditDetails model mobile = { label = text <| if g.wheel.viewContent then - "Ranger " ++ Sound.toString s + "Ranger " ++ Sound.getPath s else - "Voir " ++ Sound.toString s + "Voir " ++ Sound.getPath s , onPress = Just <| WheelMsgs [ ( wId, Wheel.ToggleContentView ) ] } @@ -1776,7 +1780,7 @@ viewEditDetails model mobile = let ok = model.newSampleName - /= Sound.toString s + /= Sound.getPath s && (not <| String.isEmpty model.newSampleName) in [ Input.button @@ -2654,7 +2658,7 @@ interactSelectEdit event mobile model = Content.S s -> let ( wave, cmd ) = - Waveform.update (Waveform.ChgSound <| Sound.toString s) model.wave + Waveform.update (Waveform.ChgSound <| Sound.getPath s) model.wave in Just ( { model | edit = [ id ], wave = wave }, Cmd.map WaveMsg cmd ) @@ -2663,7 +2667,7 @@ interactSelectEdit event mobile model = Just one -> let ( wave, cmd ) = - Waveform.update (Waveform.ChgSound one.soundName) model.wave + Waveform.update (Waveform.ChgSound one.path) model.wave in Just ( { model | edit = [ id ], beadCursor = 0, wave = wave }, Cmd.map WaveMsg cmd ) diff --git a/src/Engine.elm b/src/Engine.elm index 7b61ebb..a3b4ffa 100644 --- a/src/Engine.elm +++ b/src/Engine.elm @@ -109,7 +109,7 @@ encodeWheel w hasView parentUid = ] ++ (case Wheel.getWheelContent w of Content.S s -> - [ ( "soundName", E.string <| Sound.toString s ) + [ ( "soundPath", E.string <| Sound.getPath s ) , ( "loopPercents", E.list E.float <| Sound.getLoopPercentsList s ) ] diff --git a/src/Main.elm b/src/Main.elm index 4c078a7..94b34d6 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -295,7 +295,7 @@ update msg model = else let path = - List.concatMap (String.split "\\") <| String.split "/" <| Sound.toString s + List.concatMap (String.split "\\") <| String.split "/" <| Sound.getPath s in case path of [] -> @@ -966,7 +966,7 @@ viewSoundInLib model s id playing loading = row [ spacing 5 ] ([ Input.button [ Font.color <| - if List.any ((==) <| String.join "/" id) <| List.map Sound.toString model.loadedSoundList then + if List.any ((==) <| String.join "/" id) <| List.map Sound.getPath model.loadedSoundList then rgb 0.2 0.8 0.2 else if loading then @@ -1056,8 +1056,8 @@ viewLoaded model = } ] ++ (List.map (soundView model.showDirLoad) <| - List.sortWith (\s t -> Natural.compare (Sound.toString s) (Sound.toString t)) <| - filterFiles model.fileFilter Sound.toString model.loadedSoundList + List.sortWith (\s t -> Natural.compare (Sound.getPath s) (Sound.getPath t)) <| + filterFiles model.fileFilter Sound.getPath model.loadedSoundList ) ) ] @@ -1067,7 +1067,7 @@ soundView : Bool -> Sound -> Element Msg soundView showDir s = let fullPath = - cutExtension <| Sound.toString s + cutExtension <| Sound.getPath s l = List.concatMap (String.split "/") <| String.split "\\" fullPath diff --git a/src/Sound.elm b/src/Sound.elm index 1a64c36..30989fa 100644 --- a/src/Sound.elm +++ b/src/Sound.elm @@ -31,22 +31,11 @@ length (S s) = s.duration * (s.endPercent - s.startPercent) -toString : Sound -> String -toString (S { path }) = +getPath : Sound -> String +getPath (S { path }) = path -fileName : Sound -> String -fileName (S { path }) = - Maybe.withDefault "" <| - List.head <| - String.split "." <| - Maybe.withDefault "" <| - List.head <| - List.reverse <| - String.split "/" path - - getLoopPercentsList : Sound -> List Float getLoopPercentsList (S { startPercent, endPercent }) = [ startPercent, endPercent ] From 64010cfb94dbe557d944de6d758ed553d1e4cc42 Mon Sep 17 00:00:00 2001 From: cbossut Date: Fri, 23 Apr 2021 19:04:18 +0200 Subject: [PATCH 32/43] Fix copy (harmo and clone) --- src/Data/Mobile.elm | 2 +- src/Harmony.elm | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/Data/Mobile.elm b/src/Data/Mobile.elm index 09911bd..2124268 100644 --- a/src/Data/Mobile.elm +++ b/src/Data/Mobile.elm @@ -79,7 +79,7 @@ copy harmo move id coll = Harmo.makeCopy id newId newColl else - Harmo.toRate getWheeledContentLength newId newColl + Harmo.toRate getWheeledContentLength newId <| Harmo.hardEmptySelf newId newColl toDrawLink : Coll Geer -> Link Geer -> DrawLink diff --git a/src/Harmony.elm b/src/Harmony.elm index c13fac5..9a2b6ad 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -109,6 +109,20 @@ clean id coll = Debug.todo "Clean Base" +hardEmptySelf : Id (Harmonized g) -> Coll (Harmonized g) -> Coll (Harmonized g) +hardEmptySelf id coll = + let + harmo = + getHarmo id coll + in + case harmo.ref of + Self { unit } -> + Coll.update id (\g -> { g | harmony = { harmo | ref = Self { unit = unit, group = [], links = [] } } }) coll + + _ -> + coll + + changeSelfUnit : Id (Harmonized g) -> SelfUnit -> Coll (Harmonized g) -> Coll (Harmonized g) changeSelfUnit id su coll = let @@ -189,7 +203,7 @@ makeCopy id newId coll = in coll |> Coll.update newId (\g -> { g | harmony = newHarmo }) - |> Coll.update id (insert newId >> addLink link) + |> Coll.update baseId (insert newId >> addLink link) getLengthId : (Harmonized g -> Float) -> Id (Harmonized g) -> Coll (Harmonized g) -> Float From 5c5b902cfade33c7fc0b08bc2a6c1823566387e9 Mon Sep 17 00:00:00 2001 From: cbossut Date: Fri, 23 Apr 2021 23:02:46 +0200 Subject: [PATCH 33/43] Weave Beads And refactor link drawing and computing --- src/Data/Collar.elm | 10 +++ src/Data/Mobile.elm | 16 ++-- src/Data/Wheel.elm | 13 ++- src/Editor/Mobile.elm | 205 +++++++++++++++++++++++++++++++++--------- src/Link.elm | 67 ++++++++++---- 5 files changed, 238 insertions(+), 73 deletions(-) diff --git a/src/Data/Collar.elm b/src/Data/Collar.elm index 30e651b..1a884cd 100644 --- a/src/Data/Collar.elm +++ b/src/Data/Collar.elm @@ -49,6 +49,16 @@ fromWheel w l = } +fromBeads : Beed -> List Beed -> Colleer +fromBeads head rest = + { matrice = List.length rest + 1 + , loop = 0 + , head = head + , beads = rest + , oneSound = Nothing + } + + fromWheelMult : Wheel -> Int -> Float -> Colleer fromWheelMult w m l = { matrice = m diff --git a/src/Data/Mobile.elm b/src/Data/Mobile.elm index 2124268..c25e617 100644 --- a/src/Data/Mobile.elm +++ b/src/Data/Mobile.elm @@ -8,7 +8,7 @@ import Data.Wheel as Wheel exposing (Conteet, Wheel) import Harmony as Harmo exposing (Harmony) import Json.Decode as D import Json.Encode as E -import Link exposing (DrawLink, Link) +import Link exposing (Circle) import Math.Vector2 as Vec exposing (Vec2) import Motor @@ -82,19 +82,13 @@ copy harmo move id coll = Harmo.toRate getWheeledContentLength newId <| Harmo.hardEmptySelf newId newColl -toDrawLink : Coll Geer -> Link Geer -> DrawLink -toDrawLink coll l = +toCircle : Coll Geer -> Id Geer -> Circle +toCircle coll id = let - get id = + g = Coll.get id coll - - toCircle g = - { c = g.pos, d = getLength g coll } - - f = - get >> toCircle in - Tuple.mapBoth f f l + { c = g.pos, d = getLength g coll } getLengthId : Id Geer -> Coll Geer -> Float diff --git a/src/Data/Wheel.elm b/src/Data/Wheel.elm index 8c39a8c..c34339d 100644 --- a/src/Data/Wheel.elm +++ b/src/Data/Wheel.elm @@ -107,6 +107,7 @@ type alias Style = { mod : Mod , motor : Bool , dashed : Bool + , weaving : Bool , baseColor : Maybe Float , named : Maybe String } @@ -117,6 +118,7 @@ defaultStyle = { mod = None , motor = False , dashed = False + , weaving = False , baseColor = Nothing , named = Nothing } @@ -308,7 +310,16 @@ view w pos lengthTmp style mayWheelInter mayHandleInter uid = (\( inter, l ) -> ( Interact.hoverEvents <| inter l, Interact.draggableEvents <| inter l )) mayWheelInter in - S.g [ SA.transform [ Translate (getX pos) (getY pos) ] ] <| + S.g + (SA.transform [ Translate (getX pos) (getY pos) ] + :: (if style.weaving then + [ SA.opacity <| Opacity 0.5 ] + + else + [] + ) + ) + <| (case style.named of Just name -> [ S.text_ diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 04afc72..b5a0737 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -25,7 +25,7 @@ import Html.Events import Interact exposing (Interact) import Json.Decode as D import Json.Encode as E -import Link exposing (Link) +import Link exposing (DrawLink, Link, Segment) import Math.Vector2 as Vec exposing (Vec2, getX, getY, vec2) import Motor import Pack exposing (Pack, Packed) @@ -146,6 +146,7 @@ type Dragging | HalfLink ( Id Geer, Vec2 ) | CompleteLink (Link Geer) | Cut ( Vec2, Vec2 ) (List (Link Geer)) + | WeaveBeads Segment (List (Id Geer)) | Alterning Identifier (Maybe Identifier) BlinkState | VolumeChange | SizeChange @@ -293,6 +294,9 @@ update msg ( model, mobile ) = Cut _ _ -> NoDrag + WeaveBeads _ _ -> + NoDrag + _ -> case tool of Edit _ -> @@ -1190,6 +1194,14 @@ viewContent ( model, mobile ) = _ -> Wheel.None + isWeaving id = + case model.dragging of + WeaveBeads _ ids -> + List.member id ids + + _ -> + False + {--TODO bead mod inside collar (Wheel.view, viewContent) getMod : Int -> Wheel.Mod getMod i = @@ -1273,6 +1285,7 @@ viewContent ( model, mobile ) = { mod = getMod id , motor = id == mobile.motor , dashed = Harmo.hasHarmonics g.harmony + , weaving = isWeaving id , baseColor = Maybe.map (\bId -> (Coll.get bId mobile.gears).wheel.color) <| Harmo.getBaseId g.harmony @@ -1297,19 +1310,19 @@ viewContent ( model, mobile ) = ) ++ (case model.dragging of HalfLink ( id, pos ) -> - let - g = - Coll.get id mobile.gears - in case model.tool of Play _ _ -> let - length = - Mobile.getLength g mobile.gears + circle = + Mobile.toCircle mobile.gears id in - [ Link.drawMotorLink ( ( g.pos, length ), ( pos, length ) ) ] + [ Link.drawMotorLink ( circle, { circle | c = pos } ) ] Harmonize -> + let + g = + Coll.get id mobile.gears + in [ Link.drawRawLink ( g.pos, pos ) (Mobile.getLength g mobile.gears) @@ -1336,10 +1349,10 @@ viewContent ( model, mobile ) = CompleteLink l -> case model.tool of Play _ _ -> - Link.viewMotorLink False <| Mobile.toDrawLink mobile.gears l + Link.viewMotorLink False <| toDrawLink mobile.gears l Harmonize -> - Link.viewFractLink (Mobile.toDrawLink mobile.gears l) <| ILink l + Link.viewFractLink (toDrawLink mobile.gears l) <| ILink l _ -> [] @@ -1347,6 +1360,9 @@ viewContent ( model, mobile ) = Cut seg _ -> [ Link.drawCut seg <| PanSvg.getScale model.svg ] + WeaveBeads seg _ -> + [ Link.drawCut seg <| PanSvg.getScale model.svg ] + Content ( p, l ) -> [ S.circle [ SA.cx <| Num <| Vec.getX p @@ -1384,7 +1400,7 @@ viewContent ( model, mobile ) = List.concatMap (\l -> Link.viewMotorLink (List.any (Link.equal l) cuts) <| - Mobile.toDrawLink mobile.gears l + toDrawLink mobile.gears l ) <| Motor.getAllLinks mobile.gears @@ -1392,7 +1408,7 @@ viewContent ( model, mobile ) = Harmonize -> (case Interact.getInteract model.interact of Just ( ILink l, _ ) -> - Link.viewFractOnLink (Mobile.toDrawLink mobile.gears l) <| + Link.viewFractOnLink (toDrawLink mobile.gears l) <| Fract.simplify <| Fract.division (Coll.get (Tuple.second l) mobile.gears).harmony.fract @@ -1401,13 +1417,13 @@ viewContent ( model, mobile ) = _ -> [] ) - ++ (List.concatMap (\l -> Link.viewFractLink (Mobile.toDrawLink mobile.gears l) (ILink l)) <| + ++ (List.concatMap (\l -> Link.viewFractLink (toDrawLink mobile.gears l) (ILink l)) <| List.concatMap (.harmony >> Harmo.getLinks) <| Coll.values mobile.gears ) ++ (case model.link of Just { link, fractInput } -> - Link.viewSelectedLink (Mobile.toDrawLink mobile.gears link) <| + Link.viewSelectedLink (toDrawLink mobile.gears link) <| case fractInput of FractionInput _ _ _ -> Just <| @@ -2158,10 +2174,23 @@ uncollar id m = Nothing -computeCuts : ( Vec2, Vec2 ) -> Coll Geer -> List (Link Geer) +toDrawLink : Coll Geer -> Link Geer -> DrawLink +toDrawLink gears = + let + toC = + Mobile.toCircle gears + in + Tuple.mapBoth toC toC + + +computeCuts : Segment -> Coll Geer -> List (Link Geer) -> List (Link Geer) computeCuts cut gears = - Motor.getAllLinks gears - |> List.filter (Link.cuts cut << Link.toSegment << Mobile.toDrawLink gears) + List.filter <| Link.cuts cut << Link.toSegment << toDrawLink gears + + +computeTouch : Segment -> Coll Geer -> List (Id Geer) +computeTouch weave gears = + List.filter (Link.touchCircle weave << Mobile.toCircle gears) <| Coll.ids gears @@ -2430,13 +2459,13 @@ manageInteractEvent event model mobile = ret Nothing -> - case interactSelectEdit event mobile model of - Just ( newModel, cmd ) -> + case interactEdit event model mobile of + Just { newModel, newMobile, toUndo, cmd } -> let ret = - update StopGear ( newModel, mobile ) + update StopGear ( newModel, newMobile ) in - { ret | cmd = Cmd.batch [ cmd, ret.cmd ] } + { ret | toUndo = toUndo, cmd = Cmd.batch [ cmd, ret.cmd ] } Nothing -> case model.edit of @@ -2483,10 +2512,26 @@ interactPlay on event model mobile = -- CUT ( ISurface, Interact.Dragged { oldPos, newPos } ZSurface _, NoDrag ) -> - { return | model = { model | dragging = Cut ( oldPos, newPos ) <| computeCuts ( oldPos, newPos ) mobile.gears } } + { return + | model = + { model + | dragging = + Cut ( oldPos, newPos ) <| + computeCuts ( oldPos, newPos ) mobile.gears <| + Motor.getAllLinks mobile.gears + } + } ( ISurface, Interact.Dragged { newPos } ZSurface _, Cut ( p1, _ ) _ ) -> - { return | model = { model | dragging = Cut ( p1, newPos ) <| computeCuts ( p1, newPos ) mobile.gears } } + { return + | model = + { model + | dragging = + Cut ( p1, newPos ) <| + computeCuts ( p1, newPos ) mobile.gears <| + Motor.getAllLinks mobile.gears + } + } ( ISurface, Interact.DragEnded True, Cut _ cuts ) -> let @@ -2646,12 +2691,25 @@ interactHarmonize event model mobile = return -interactSelectEdit : Interact.Event Interactable Zone -> Mobeel -> Model -> Maybe ( Model, Cmd Msg ) -interactSelectEdit event mobile model = - case ( event.item, event.action ) of - ( IWheel ( id, _ ), Interact.Clicked ( _, False, False ) ) -> +interactEdit : + Interact.Event Interactable Zone + -> Model + -> Mobeel + -> Maybe { newModel : Model, newMobile : Mobeel, toUndo : ToUndo, cmd : Cmd Msg } +interactEdit event model mobile = + let + return = + { newModel = model + , newMobile = mobile + , toUndo = NOOP + , cmd = Cmd.none + } + in + case ( event.item, event.action, model.dragging ) of + -- SIMPLE CLIC + ( IWheel ( id, _ ), Interact.Clicked ( _, False, False ), _ ) -> if model.edit == [ id ] then - Just ( { model | edit = [] }, Cmd.none ) + Just { return | newModel = { model | edit = [] } } else case Wheel.getContent <| Coll.get id mobile.gears of @@ -2660,7 +2718,7 @@ interactSelectEdit event mobile model = ( wave, cmd ) = Waveform.update (Waveform.ChgSound <| Sound.getPath s) model.wave in - Just ( { model | edit = [ id ], wave = wave }, Cmd.map WaveMsg cmd ) + Just { return | newModel = { model | edit = [ id ], wave = wave }, cmd = Cmd.map WaveMsg cmd } Content.C c -> case c.oneSound of @@ -2669,30 +2727,93 @@ interactSelectEdit event mobile model = ( wave, cmd ) = Waveform.update (Waveform.ChgSound one.path) model.wave in - Just ( { model | edit = [ id ], beadCursor = 0, wave = wave }, Cmd.map WaveMsg cmd ) + Just + { return + | newModel = { model | edit = [ id ], beadCursor = 0, wave = wave } + , cmd = Cmd.map WaveMsg cmd + } Nothing -> - Just ( { model | edit = [ id ], beadCursor = 0 }, Cmd.none ) + Just { return | newModel = { model | edit = [ id ], beadCursor = 0 } } _ -> - Just ( { model | edit = [ id ] }, Cmd.none ) + Just { return | newModel = { model | edit = [ id ] } } - ( IWheel ( id, _ ), Interact.Clicked _ ) -> + -- CTRL/CMD/SHIFT CLIC + ( IWheel ( id, _ ), Interact.Clicked _, _ ) -> let already = List.foldl (\el -> (||) <| el == id) False model.edit in Just - ( { model - | edit = - if already then - List.filter ((/=) id) model.edit + { return + | newModel = + { model + | edit = + if already then + List.filter ((/=) id) model.edit - else - id :: model.edit - } - , Cmd.none - ) + else + id :: model.edit + } + } + + -- WEAVE + ( ISurface, Interact.Dragged { oldPos, newPos } ZSurface _, NoDrag ) -> + Just + { return + | newModel = + { model + | dragging = + WeaveBeads ( oldPos, newPos ) <| + computeTouch ( oldPos, newPos ) mobile.gears + } + } + + ( ISurface, Interact.Dragged { newPos } ZSurface _, WeaveBeads ( p1, _ ) _ ) -> + Just + { return + | newModel = + { model + | dragging = + WeaveBeads ( p1, newPos ) <| + computeTouch ( p1, newPos ) mobile.gears + } + } + + ( ISurface, Interact.DragEnded True, WeaveBeads _ ids ) -> + let + ( beads, positions ) = + List.unzip <| + List.map + (\g -> + ( { length = Harmo.getLength CommonData.getWheeledContentLength g mobile.gears + , wheel = g.wheel + } + , g.pos + ) + ) + <| + List.sortBy (Vec.getX << .pos) <| + List.map (\id -> Coll.get id mobile.gears) ids + in + case beads of + [] -> + Nothing + + head :: tail -> + let + newGear = + Mobile.gearFromContent (Content.C <| Collar.fromBeads head tail) <| + Vec.scale (1 / (toFloat <| List.length ids)) <| + List.foldl Vec.add (vec2 0 0) positions + in + Just + { return + | newModel = { model | dragging = NoDrag } + , newMobile = { mobile | gears = Coll.insert newGear mobile.gears } + , toUndo = Do + } _ -> Nothing diff --git a/src/Link.elm b/src/Link.elm index 231e175..68326cf 100644 --- a/src/Link.elm +++ b/src/Link.elm @@ -32,6 +32,10 @@ type alias Circle = { d : Float, c : Vec2 } +type alias Segment = + ( Vec2, Vec2 ) + + type alias DrawLink = ( Circle, Circle ) @@ -97,7 +101,7 @@ viewSelectedLink ( e, f ) mayFract = viewMotorLink : Bool -> DrawLink -> List (Svg msg) -viewMotorLink cutting ( e, f ) = +viewMotorLink cutting dl = [ S.g [ SA.opacity <| TypedSvg.Types.Opacity <| @@ -107,34 +111,41 @@ viewMotorLink cutting ( e, f ) = else 1 ] - [ drawMotorLink - ( ( e.c, e.d ) - , ( f.c, f.d ) - ) - ] + [ drawMotorLink dl ] ] -drawMotorLink : ( ( Vec2, Float ), ( Vec2, Float ) ) -> Svg msg -drawMotorLink ( ( p1, d1 ), ( p2, d2 ) ) = +drawMotorLink : DrawLink -> Svg msg +drawMotorLink ( c1, c2 ) = let - dir = - Vec.direction p2 p1 + p1 = + c1.c - contactPoint center diameter clockWise = - Vec.add center <| - Vec.scale (diameter / 2) (rotate90 dir clockWise) + d1 = + c1.d + + p2 = + c2.c + + d2 = + c2.d gearL = d1 + d2 / 2 + + ( a1, b1 ) = + getPerpandicularDiameter c1 ( p1, p2 ) + + ( a2, b2 ) = + getPerpandicularDiameter c2 ( p1, p2 ) in S.g [] - [ drawRawLink ( contactPoint p1 d1 True, contactPoint p2 d2 True ) gearL baseColor - , drawRawLink ( contactPoint p1 d1 False, contactPoint p2 d2 False ) gearL baseColor + [ drawRawLink ( a1, a2 ) gearL baseColor + , drawRawLink ( b1, b2 ) gearL baseColor ] -drawRawLink : ( Vec2, Vec2 ) -> Float -> Color -> Svg msg +drawRawLink : Segment -> Float -> Color -> Svg msg drawRawLink ( p1, p2 ) gearL c = S.polyline [ SA.points [ tupleFromVec p1, tupleFromVec p2 ] @@ -145,7 +156,7 @@ drawRawLink ( p1, p2 ) gearL c = [] -drawCut : ( Vec2, Vec2 ) -> Float -> Svg msg +drawCut : Segment -> Float -> Svg msg drawCut ( p1, p2 ) scale = S.polyline [ SA.points [ tupleFromVec p1, tupleFromVec p2 ] @@ -179,7 +190,7 @@ equal l1 l2 = || (Tuple.first l1 == Tuple.second l2 && Tuple.first l2 == Tuple.second l1) -toSegment : DrawLink -> ( Vec2, Vec2 ) +toSegment : DrawLink -> Segment toSegment l = Tuple.mapBoth .c .c l @@ -198,11 +209,24 @@ rotate90 v clockWise = vec2 -(Vec.getY v) (Vec.getX v) +getPerpandicularDiameter : Circle -> Segment -> Segment +getPerpandicularDiameter { c, d } ( p1, p2 ) = + let + dir = + Vec.direction p2 p1 + + projection center diameter clockWise = + Vec.add center <| + Vec.scale (diameter / 2) (rotate90 dir clockWise) + in + ( projection c d True, projection c d False ) + + -- from https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect -cuts : ( Vec2, Vec2 ) -> ( Vec2, Vec2 ) -> Bool +cuts : Segment -> Segment -> Bool cuts ( p, p2 ) ( q, q2 ) = let r = @@ -235,6 +259,11 @@ cuts ( p, p2 ) ( q, q2 ) = False +touchCircle : Segment -> Circle -> Bool +touchCircle segment circle = + cuts segment <| getPerpandicularDiameter circle segment + + crossProductLength : Vec2 -> Vec2 -> Float crossProductLength v w = Vec.getX v * Vec.getY w - Vec.getY v * Vec.getX w From c71f60de74b9c53a7c85489ffbce508d655ed03f Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 01:10:27 +0200 Subject: [PATCH 34/43] Finish shortcuts for waveform --- src/Doc.elm | 5 +++ src/Editor/Mobile.elm | 5 +++ src/Main.elm | 27 +++++++++------ src/Waveform.elm | 80 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 100 insertions(+), 17 deletions(-) diff --git a/src/Doc.elm b/src/Doc.elm index 7b64c3a..94dc97a 100644 --- a/src/Doc.elm +++ b/src/Doc.elm @@ -304,6 +304,11 @@ keyCodeToShortcut model = Dict.map (always Editor) <| Editor.keyCodeToShortcut model.editor <| getViewing model +keyCodeToDirection : Dict String Msg +keyCodeToDirection = + Dict.map (always MobileMsg) Editor.keyCodeToDirection + + view : Model -> Element Msg view doc = row [ height fill, width fill ] <| diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 0788f91..61b0610 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -137,6 +137,11 @@ keyCodeToShortcut mod mob = Dict.map (always WaveMsg) <| Waveform.keyCodeToShortcut <| getWavePoints mod mob +keyCodeToDirection : Dict String Msg +keyCodeToDirection = + Dict.map (always WaveMsg) Waveform.keyCodeToDirection + + type alias LinkInfo = { link : Link Geer, fractInput : FractInput } diff --git a/src/Main.elm b/src/Main.elm index 603636c..ba5308b 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -721,12 +721,12 @@ update msg model = Keys.Repeat code -> case Dict.get code keyCodeToDirection of - Just dir -> + Just dirMsg -> let - ( doc, cmd ) = - Doc.update (Doc.DirectionRepeat dir) m.doc + ( dirModel, cmd ) = + update dirMsg m in - ( { m | doc = doc }, Cmd.batch [ c, Cmd.map DocMsg cmd ] ) + ( dirModel, Cmd.batch [ c, cmd ] ) Nothing -> ( m, c ) @@ -789,14 +789,19 @@ keyCodeToShortcut model = Doc.keyCodeToShortcut model.doc -keyCodeToDirection : Dict String PanSvg.Direction +keyCodeToDirection : Dict String Msg keyCodeToDirection = - Dict.fromList - [ ( "KeyO", PanSvg.Up ) - , ( "KeyK", PanSvg.Left ) - , ( "KeyL", PanSvg.Down ) - , ( "Semicolon", PanSvg.Right ) - ] + Dict.union + (Dict.map (always <| DocMsg << Doc.DirectionRepeat) <| + Dict.fromList + [ ( "KeyO", PanSvg.Up ) + , ( "KeyK", PanSvg.Left ) + , ( "KeyL", PanSvg.Down ) + , ( "Semicolon", PanSvg.Right ) + ] + ) + <| + Dict.map (always DocMsg) Doc.keyCodeToDirection diff --git a/src/Waveform.elm b/src/Waveform.elm index 41c1229..a4a7b01 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -98,9 +98,11 @@ isDrawn { drawn } name = type Mark - = Cursor Cursor - | Start - | End + = MStart + | MEnd + | MLoopStart + | MLoopEnd + | MDiv Int type Msg @@ -188,10 +190,55 @@ update msg wave = ( newWave, requestRedraw newWave ) MoveView d -> - update (ChgView wave.zoomFactor (wave.startPercent + mapPxToSoundPercent wave d)) wave + update (ChgView wave.zoomFactor <| wave.startPercent + pxToSoundDist wave d) wave CenterOn cur mayCursors -> - Debug.todo "center on cursors" + let + up pos = + update (ChgView wave.zoomFactor <| pos - 0.5 / wave.zoomFactor) wave + in + case cur of + MStart -> + up 0 + + MEnd -> + up 1 + + _ -> + case mayCursors of + Just (Sound { start, end, offset }) -> + case cur of + MLoopStart -> + up start + + MLoopEnd -> + up end + + MDiv _ -> + up offset + + _ -> + ( wave, Cmd.none ) + + Just (CollarDiv { start, end, divs }) -> + case cur of + MLoopStart -> + up start + + MLoopEnd -> + up end + + MDiv i -> + Maybe.withDefault ( wave, Cmd.none ) <| + Maybe.map up <| + List.head <| + List.drop i divs + + _ -> + ( wave, Cmd.none ) + + Nothing -> + ( wave, Cmd.none ) ZoomPoint delta x -> let @@ -320,12 +367,33 @@ sub = keyCodeToShortcut : Maybe Cursors -> Dict String Msg keyCodeToShortcut mayC = + let + msg mark = + CenterOn mark mayC + in + Dict.fromList <| + ( "Backquote", msg MStart ) + :: List.concatMap (\( k1, k2, m ) -> [ ( k1, m ), ( k2, m ) ]) + [ ( "Digit0", "Numpad0", msg MStart ) + , ( "Digit9", "Numpad9", msg MEnd ) + , ( "Digit1", "Numpad1", msg MLoopStart ) + , ( "Digit2", "Numpad2", msg <| MDiv 0 ) + , ( "Digit3", "Numpad3", msg <| MDiv 1 ) + , ( "Digit4", "Numpad4", msg <| MDiv 2 ) + , ( "Digit5", "Numpad5", msg <| MDiv 3 ) + , ( "Digit6", "Numpad6", msg <| MDiv 4 ) + , ( "Digit7", "Numpad7", msg <| MDiv 5 ) + , ( "Digit8", "Numpad8", msg MLoopEnd ) + ] + + +keyCodeToDirection : Dict String Msg +keyCodeToDirection = Dict.fromList [ ( "KeyG", Zoom False ) , ( "KeyH", Zoom True ) , ( "KeyB", MoveView -20 ) , ( "KeyN", MoveView 20 ) - , ( "Key1", CenterOn Start mayC ) ] From fba3d7eb90c80d39f8e4c47d2e7bb24d28f208e5 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 13:16:28 +0200 Subject: [PATCH 35/43] Fix uncollar when only one bead after delete --- src/Data/Common.elm | 28 ++++++++++++++++++++-------- src/Editor/Mobile.elm | 17 ++++++++--------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/Data/Common.elm b/src/Data/Common.elm index 8614d0a..2b5ef1f 100644 --- a/src/Data/Common.elm +++ b/src/Data/Common.elm @@ -85,42 +85,54 @@ deleteWheel : -> Mobile Wheel -> (Id (Gear Wheel) -> Mobile Wheel -> Mobile Wheel) -> (Int -> Collar Wheel -> Collar Wheel) - -> Mobile Wheel + -> ( Mobile Wheel, Bool ) -- True if there is only one bead left after deleting deleteWheel ( id, l ) mobile gRm bRm = let - rec : Int -> List Int -> Collar Wheel -> Collar Wheel + rec : Int -> List Int -> Collar Wheel -> ( Collar Wheel, Bool ) rec index list col = case list of [] -> - bRm index col + let + newCol = + bRm index col + in + ( newCol, (List.length <| Content.getBeads newCol) == 1 ) i :: rest -> case Wheel.getContent <| Content.getBead index col of Content.C subCol -> - Content.updateBead index (Wheel.setContent <| Content.C <| rec i rest subCol) col + let + ( newSubCol, last ) = + rec i rest subCol + in + ( Content.updateBead index (Wheel.setContent <| Content.C newSubCol) col, last ) _ -> let _ = Debug.log "Wrong identifier to delete bead" ( id, l, mobile ) in - col + ( col, False ) in case l of [] -> - gRm id mobile + ( gRm id mobile, False ) i :: rest -> case Wheel.getContent <| Coll.get id mobile.gears of Content.C col -> - Content.updateGear id (Wheel.setContent <| Content.C <| rec i rest col) mobile + let + ( newCol, last ) = + rec i rest col + in + ( Content.updateGear id (Wheel.setContent <| Content.C newCol) mobile, last ) _ -> let _ = Debug.log "Wrong identifier to delete bead" ( id, l, mobile ) in - mobile + ( mobile, False ) updateWheel : Identifier -> Wheel.Msg -> Mobile Wheel -> Mobile Wheel diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 61b0610..aedcb07 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -489,18 +489,17 @@ update msg ( model, mobile ) = DeleteWheel ( id, l ) -> let - tmp = + ( tmp, toUncollar ) = CommonData.deleteWheel ( id, l ) mobile Mobile.rm Collar.rm newMob = - case l of - [] -> - tmp + if toUncollar then + Maybe.withDefault tmp <| + Maybe.map Tuple.first <| + uncollar ( id, List.take (List.length l - 1) l ) tmp - _ -> - Maybe.withDefault tmp <| - Maybe.map Tuple.first <| - uncollar ( id, List.take (List.length l - 1) l ) tmp + else + tmp in { return | model = @@ -2141,7 +2140,7 @@ uncollar id m = m.motor newMob = - CommonData.deleteWheel id { m | motor = newMotor } Mobile.rm Collar.rm + Tuple.first <| CommonData.deleteWheel id { m | motor = newMotor } Mobile.rm Collar.rm ( gearPos, gearLength ) = Mobile.gearPosSize gId m.gears From f63e11ffe7ce7e0247571052fb749616b415c626 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 14:23:54 +0200 Subject: [PATCH 36/43] Erase Suppr Shortcut --- src/Doc.elm | 9 --------- src/Main.elm | 2 -- 2 files changed, 11 deletions(-) diff --git a/src/Doc.elm b/src/Doc.elm index 94dc97a..4f234b0 100644 --- a/src/Doc.elm +++ b/src/Doc.elm @@ -58,7 +58,6 @@ type Shortcut | Play | Left | Right - | Suppr | Pack | Editor Editor.Msg @@ -216,14 +215,6 @@ update msg doc = _ -> ( doc, Cmd.none ) - Suppr -> - case ( doc.editor.edit, doc.editor.tool ) of - ( [ id ], Editor.Edit _ ) -> - update (MobileMsg <| Editor.DeleteWheel ( id, [] )) doc - - _ -> - ( doc, Cmd.none ) - Left -> update (MobileMsg <| Editor.CursorLeft) doc diff --git a/src/Main.elm b/src/Main.elm index ba5308b..c70915d 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -780,8 +780,6 @@ keyCodeToShortcut model = , ( "Space", Doc.Play ) , ( "ArrowLeft", Doc.Left ) , ( "ArrowRight", Doc.Right ) - , ( "Backspace", Doc.Suppr ) - , ( "Delete", Doc.Suppr ) , ( "KeyT", Doc.Pack ) ] ) From 99040bc5528e015bdc9edf46fa39e7bbfe35d52f Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 14:24:38 +0200 Subject: [PATCH 37/43] reset Wave when changing sel even if same sound --- src/Waveform.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Waveform.elm b/src/Waveform.elm index a4a7b01..e71e974 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -131,7 +131,7 @@ update msg wave = ChgSound name -> if wave.drawn == SoundDrawn name then - ( wave, Cmd.none ) + ( { wave | zoomFactor = init.zoomFactor, startPercent = init.startPercent }, Cmd.none ) else let From 96f270e0b4093ec09199f11bea6c0344bce310ac Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 14:26:43 +0200 Subject: [PATCH 38/43] clean getNameWithDefault in favor of Common.getName --- src/Editor/Mobile.elm | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index aedcb07..d1ce527 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -1514,23 +1514,6 @@ viewContent ( model, mobile ) = --- TODO duplicate with Gear.getName and Common.getName - - -getNameWithDefault : Id Geer -> Mobeel -> String -getNameWithDefault id mobile = - let - w = - (Coll.get id mobile.gears).wheel - in - if String.isEmpty w.name then - Gear.toUID id - - else - w.name - - - -- TODO split in functions for each component, and maybe move to another file, like Interacting, or good old common @@ -1551,7 +1534,7 @@ viewDetails model mobile = case model.mode of ChangeSound id -> [ viewDetailsColumn (rgb 0.5 0.2 0) <| - [ text <| getNameWithDefault id mobile + [ text <| CommonData.getName ( id, [] ) mobile , text "Choisir un son chargé" , Input.button [] { label = text "Annuler" @@ -1808,7 +1791,7 @@ viewEditDetails model mobile = ++ Harmo.view id mobile.gears (\rId -> - getNameWithDefault rId mobile + CommonData.getName ( rId, [] ) mobile ) , text <| "( " ++ (Round.round 2 <| Mobile.getLengthId id mobile.gears) ++ " )" , text <| @@ -1854,7 +1837,7 @@ viewEditDetails model mobile = _ :: _ -> [ viewDetailsColumn (rgb 0.5 0.5 0.5) <| - (List.map (\id -> text <| getNameWithDefault id mobile) <| List.reverse model.edit) + (List.map (\id -> text <| CommonData.getName ( id, [] ) mobile) <| List.reverse model.edit) ++ [ Input.button [] { label = text "Encapsuler" , onPress = Just <| Capsuled <| List.reverse model.edit From e2ac279345a04b04cf0e5e911435b261a1950f15 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 14:28:17 +0200 Subject: [PATCH 39/43] clean Normal mode from ChgSound in right func --- src/Editor/Mobile.elm | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index d1ce527..b7e2d2a 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -2033,10 +2033,6 @@ doChangeContent id c mayColor model mobile = gears = List.foldl (\el -> Coll.update el chSound) tmpGears group - - newModel = - -- TODO Why !!?? - { model | mode = Normal } in case mayColor of Just color -> @@ -2047,7 +2043,6 @@ doChangeContent id c mayColor model mobile = { return | mobile = { mobile | gears = List.foldl (\el -> Coll.update el chColor) gears group } , toUndo = Do - , model = newModel } Nothing -> @@ -2058,7 +2053,6 @@ doChangeContent id c mayColor model mobile = { return | mobile = { mobile | gears = gears } , toUndo = Group - , model = newModel , cmd = Random.generate (WheelMsgs << colorToMsgs) colorGen } @@ -2259,8 +2253,7 @@ manageInteractEvent event model mobile = ChangeSound id -> case ( event.item, event.action ) of ( ISound s, Interact.Clicked _ ) -> - -- TODO Should change mode to Normal here instead of if doChangeContent? - doChangeContent id (Content.S s) Nothing model mobile + doChangeContent id (Content.S s) Nothing { model | mode = Normal } mobile _ -> return From 34ff46375ceb8ef6cdca2f18cc122e6b6d88682d Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 14:29:33 +0200 Subject: [PATCH 40/43] fix Waving larger than sound --- src/Editor/Mobile.elm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index b7e2d2a..35fb0c4 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -2912,7 +2912,10 @@ interactMove event model mobile = Wheel.getLoopPercents waveG selPercentLength = - Mobile.getLengthId id mobile.gears * (end - start) / Mobile.getLength waveG mobile.gears + clamp 0 1 <| + Mobile.getLengthId id mobile.gears + * (end - start) + / Mobile.getLength waveG mobile.gears ( wave, cmd ) = Waveform.update (Waveform.Select ( Vec.getX oldPos, selPercentLength )) model.wave From 6951d83064d3a3cefd094aa1ec37524ef074a6f2 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 14:30:28 +0200 Subject: [PATCH 41/43] can Wave a Wheel into oneSound Collar --- src/Editor/Mobile.elm | 25 +++++++++++++++++++++++-- src/Waveform.elm | 13 +++++++------ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index 35fb0c4..f38b432 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -2946,9 +2946,30 @@ interactMove event model mobile = mayLoop = Waveform.getSelPercents model.wave + + maySound = + case Wheel.getWheelContent waveW of + Content.S s -> + Just s + + Content.C c -> + case c.oneSound of + Just _ -> + case Wheel.getContent (Collar.get 0 c) of + Content.S s -> + Just s + + _ -> + Nothing + + _ -> + Nothing + + _ -> + Nothing in - case ( mayLoop, Wheel.getWheelContent waveW ) of - ( Just ( start, end ), Content.S s ) -> + case ( mayLoop, maySound ) of + ( Just ( start, end ), Just s ) -> let ( wave, cmd ) = Waveform.update Waveform.CancelSel model.wave diff --git a/src/Waveform.elm b/src/Waveform.elm index e71e974..1eb676b 100644 --- a/src/Waveform.elm +++ b/src/Waveform.elm @@ -532,12 +532,6 @@ view wave mayCursors interState wrapInter wrapMsg = , curs c.start <| LoopStart Main , curs c.end <| LoopEnd Main , curs c.offset <| StartOffset Main - , case wave.sel of - Just points -> - sel points Nothing darkGreySel - - Nothing -> - [] ] CollarDiv c -> @@ -550,6 +544,13 @@ view wave mayCursors interState wrapInter wrapMsg = ] ++ List.indexedMap (\i div -> curs div (Divide i Main)) c.divs ) + ++ (case wave.sel of + Just points -> + sel points Nothing darkGreySel + + Nothing -> + [] + ) ) Nothing -> From f4646bad729ba80984e1d127fb1591c632763e0c Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 15:39:45 +0200 Subject: [PATCH 42/43] auto folder recs and cuts --- index.js | 6 ++++- ports.js | 11 +++++++-- src/Data/Common.elm | 13 ++-------- src/Main.elm | 60 +++++++++++++++++++++++++++++++++++++++++---- src/Sound.elm | 11 +++++++++ 5 files changed, 82 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index d8a8f28..6c88814 100644 --- a/index.js +++ b/index.js @@ -105,7 +105,11 @@ const internCallback = staticRoute({dir:__dirname, tryfiles:['ports.html']}) res.end() return; } - fs.renameSync(files.file.path, soundPath + files.file.name) + let path = soundPath + + (fields.from ? fields.from + '-' : '') + + (fields.type ? fields.type + '/' : '') + if (!fs.existsSync(path)) fs.mkdirSync(path, {recursive:true}) + fs.renameSync(files.file.path, path + files.file.name) res.end() }) } else { diff --git a/ports.js b/ports.js index cb89df3..233e887 100644 --- a/ports.js +++ b/ports.js @@ -91,7 +91,10 @@ function inputRec(args) { , start = args[1] if (name) { micRecorder.stop() - micRecorder.exportWAV(bl => app.ports.gotNewSample.send(new File([bl], name + ".wav", {type: "audio/wav"}))) + micRecorder.exportWAV(bl => app.ports.gotNewSample.send( + { type : "rec" + , file : new File([bl], name + ".wav", {type: "audio/wav"}) + })) micRecorder.clear() recording = false if (!scheduler.running) ctx.suspend() @@ -122,7 +125,11 @@ function cutSample(infos) { newBuf.copyToChannel(chan, i) } - app.ports.gotNewSample.send(new File([audioBufferToWav(newBuf)], infos.newFileName + ".wav", {type: "audio/wav"})) + app.ports.gotNewSample.send( + { type : "cut" + , from : infos.fromSoundPath + , file : new File([audioBufferToWav(newBuf)], infos.newFileName + ".wav", {type: "audio/wav"}) + }) } function engine(o) { diff --git a/src/Data/Common.elm b/src/Data/Common.elm index 2b5ef1f..c53c3fc 100644 --- a/src/Data/Common.elm +++ b/src/Data/Common.elm @@ -21,25 +21,16 @@ getName id mobile = let w = getWheel id mobile - - fileNameFromPath = - Maybe.withDefault "" - << List.head - << String.split "." - << Maybe.withDefault "" - << List.head - << List.reverse - << String.split "/" in if String.isEmpty w.name then case Wheel.getWheelContent w of Content.S s -> - fileNameFromPath <| Sound.getPath s + Sound.fileNameFromPath <| Sound.getPath s Content.C c -> case c.oneSound of Just { path } -> - fileNameFromPath path + Sound.fileNameFromPath path Nothing -> toUid id diff --git a/src/Main.elm b/src/Main.elm index c70915d..da3dd22 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -136,6 +136,11 @@ soundMimeTypes = [ "audio/x-wav", "audio/wav" ] +isValidFile : File -> Bool +isValidFile file = + List.member (File.mime file) soundMimeTypes && File.size file <= (200 * 1024 * 1024) + + -- UPDATE @@ -159,7 +164,7 @@ type Msg | EnteredNewRecName String | ClickedUploadSound | UploadSounds File (List File) - | GotNewSample (Result D.Error File) + | GotNewSample (Result D.Error ( File, SampleType )) | ClickedUploadSave | UploadSaves File (List File) | ChangedExplorerTab ExTab @@ -535,7 +540,7 @@ update msg model = , Cmd.batch <| List.map (\file -> - if List.member (File.mime file) soundMimeTypes && File.size file <= (200 * 1024 * 1024) then + if isValidFile file then Http.post { url = Url.toString model.currentUrl ++ "upSound" , body = @@ -553,8 +558,29 @@ update msg model = GotNewSample res -> case res of - Ok file -> - update (UploadSounds file []) model + Ok ( file, stype ) -> + ( model + , if isValidFile file then + Http.post + { url = Url.toString model.currentUrl ++ "upSound" + , body = + Http.multipartBody <| + Http.filePart "file" file + :: (case stype of + Reced -> + [ Http.stringPart "type" "REC" ] + + Cuted from -> + [ Http.stringPart "type" "CUT" + , Http.stringPart "from" from + ] + ) + , expect = Http.expectWhatever <| always RequestSoundList + } + + else + Cmd.none + ) Err err -> let @@ -748,12 +774,36 @@ subs { doc } = [ soundLoaded (SoundLoaded << D.decodeValue Sound.decoder) , BE.onResize (\w h -> GotScreenSize { width = w, height = h }) , micOpened <| always MicOpened - , gotNewSample <| (GotNewSample << D.decodeValue File.decoder) + , gotNewSample <| (GotNewSample << D.decodeValue sampleDecoder) ] ++ List.map (Sub.map DocMsg) (Doc.subs doc) ++ List.map (Sub.map KeysMsg) Keys.subs +type SampleType + = Reced + | Cuted String + + +sampleDecoder : D.Decoder ( File, SampleType ) +sampleDecoder = + D.map2 Tuple.pair (D.field "file" File.decoder) <| + D.andThen + (\s -> + case s of + "rec" -> + D.succeed Reced + + "cut" -> + D.map (Cuted << Sound.fileNameFromPath) <| D.field "from" D.string + + _ -> + D.fail "not valid type to decode sample" + ) + <| + D.field "type" D.string + + type Mode = EditorMode Editor.Mode -- FIXME Second source of truth, not reliable | Capsuling diff --git a/src/Sound.elm b/src/Sound.elm index 30989fa..96bd6a1 100644 --- a/src/Sound.elm +++ b/src/Sound.elm @@ -36,6 +36,17 @@ getPath (S { path }) = path +fileNameFromPath : String -> String +fileNameFromPath = + Maybe.withDefault "" + << List.head + << String.split "." + << Maybe.withDefault "" + << List.head + << List.reverse + << String.split "/" + + getLoopPercentsList : Sound -> List Float getLoopPercentsList (S { startPercent, endPercent }) = [ startPercent, endPercent ] From f0116299251dc930490bb7d6e814e75701e3c432 Mon Sep 17 00:00:00 2001 From: cbossut Date: Sat, 24 Apr 2021 15:47:12 +0200 Subject: [PATCH 43/43] minors Comments Clean unused parameters and funcs Renaming --- src/Data/Wheel.elm | 2 +- src/Editor/Mobile.elm | 28 ++++++++++++++++++++++------ src/Harmony.elm | 4 ++-- src/Sound.elm | 8 -------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Data/Wheel.elm b/src/Data/Wheel.elm index 833fb51..e5d9f9e 100644 --- a/src/Data/Wheel.elm +++ b/src/Data/Wheel.elm @@ -99,7 +99,7 @@ fromContent c = type Mod = None | Selectable - | Selected Bool + | Selected Bool -- First selected | Resizing diff --git a/src/Editor/Mobile.elm b/src/Editor/Mobile.elm index f38b432..6bb928a 100644 --- a/src/Editor/Mobile.elm +++ b/src/Editor/Mobile.elm @@ -1207,7 +1207,7 @@ viewContent ( model, mobile ) = _ -> Wheel.None - Just ( IResizeHandle iid _, mode ) -> + Just ( IResizeHandle iid _, _ ) -> if iid /= id then Wheel.None @@ -1282,6 +1282,7 @@ viewContent ( model, mobile ) = wheel = g.wheel + -- BLINK w = case model.dragging of Alterning ( idd, [] ) mayId ( b, _ ) -> @@ -1303,6 +1304,7 @@ viewContent ( model, mobile ) = _ -> wheel in + -- VIEW WHEEL Wheel.view w g.pos (Mobile.getLength g mobile.gears) @@ -1332,6 +1334,7 @@ viewContent ( model, mobile ) = <| Coll.toList mobile.gears ) + -- VIEW DRAGGING ++ (case model.dragging of HalfLink ( id, pos ) -> case model.tool of @@ -1411,6 +1414,7 @@ viewContent ( model, mobile ) = [] ) ++ (case model.tool of + -- VIEW MOTOR LINKS Play _ _ -> let cuts = @@ -1429,7 +1433,9 @@ viewContent ( model, mobile ) = <| Motor.getAllLinks mobile.gears + -- VIEW HARMO LINKS Harmonize -> + -- HOVERED FRACTION (case Interact.getInteract model.interact of Just ( ILink l, _ ) -> Link.viewFractOnLink (toDrawLink mobile.gears l) <| @@ -1441,10 +1447,12 @@ viewContent ( model, mobile ) = _ -> [] ) + -- ALL HARMO LINKS ++ (List.concatMap (\l -> Link.viewFractLink (toDrawLink mobile.gears l) (ILink l)) <| List.concatMap (.harmony >> Harmo.getLinks) <| Coll.values mobile.gears ) + -- SELECTED LINK ++ (case model.link of Just { link, fractInput } -> Link.viewSelectedLink (toDrawLink mobile.gears link) <| @@ -1463,6 +1471,7 @@ viewContent ( model, mobile ) = [] ) + -- COLLAR CURSOR Edit _ -> case model.edit of [ id ] -> @@ -2474,7 +2483,7 @@ manageInteractEvent event model mobile = g = Coll.get id mobile.gears in - case interactWave g event model mobile of + case interactWave g event model of Just (ReturnWheel subMsg) -> update (WheelMsgs [ ( ( id, [] ), subMsg ) ]) ( model, mobile ) @@ -2742,7 +2751,7 @@ interactEdit event model mobile = _ -> Just { return | newModel = { model | edit = [ id ] } } - -- CTRL/CMD/SHIFT CLIC + -- CTRL/CMD/ALT CLIC ( IWheel ( id, _ ), Interact.Clicked _, _ ) -> let already = @@ -2839,6 +2848,7 @@ interactMove event model mobile = } in case ( event.item, event.action, model.dragging ) of + -- START MOVE ( IWheel ( id, [] ), Interact.Dragged { newPos } ZSurface _, _ ) -> let gearUp = @@ -2860,9 +2870,11 @@ interactMove event model mobile = , cmd = Cmd.map WaveMsg cmd } + -- END MOVE ( _, Interact.DragEnded _, Moving ) -> Just { return | model = { model | dragging = NoDrag }, mobile = mobile, toUndo = Do } + -- START PACKING ( IWheel ( id, [] ), Interact.Dragged { newPos } ZPack _, _ ) -> Just { return @@ -2883,12 +2895,14 @@ interactMove event model mobile = } } - ( IWheel ( id, [] ), Interact.DragEnded True, Packing ) -> + -- END PACKING + ( IWheel ( _, [] ), Interact.DragEnded True, Packing ) -> Just { return | model = { model | dragging = NoDrag, pack = Pack.update Pack.PackIt model.pack } } + -- START WAVING ( IWheel _, Interact.Dragged { absD } ZWave _, Waving ) -> let ( wave, cmd ) = @@ -2901,6 +2915,7 @@ interactMove event model mobile = , cmd = Cmd.map WaveMsg cmd } + -- MOVE WAVE SEL ( IWheel ( id, [] ), Interact.Dragged { oldPos } ZWave _, _ ) -> case model.edit of [ waveId ] -> @@ -2934,6 +2949,7 @@ interactMove event model mobile = _ -> Nothing + -- END WAVING ( IWheel ( id, [] ), Interact.DragEnded True, Waving ) -> case model.edit of [ waveId ] -> @@ -3010,8 +3026,8 @@ type InteractWaveReturn | ReturnWave Waveform.Msg -interactWave : Geer -> Interact.Event Interactable Zone -> Model -> Mobeel -> Maybe InteractWaveReturn -interactWave g event model mobile = +interactWave : Geer -> Interact.Event Interactable Zone -> Model -> Maybe InteractWaveReturn +interactWave g event model = let move part = case part of diff --git a/src/Harmony.elm b/src/Harmony.elm index 9a2b6ad..9a08eab 100644 --- a/src/Harmony.elm +++ b/src/Harmony.elm @@ -207,8 +207,8 @@ makeCopy id newId coll = getLengthId : (Harmonized g -> Float) -> Id (Harmonized g) -> Coll (Harmonized g) -> Float -getLengthId f id coll = - getLength f (Coll.get id coll) coll +getLengthId getContentLength id coll = + getLength getContentLength (Coll.get id coll) coll getLength : (Harmonized g -> Float) -> Harmonized g -> Coll (Harmonized g) -> Float diff --git a/src/Sound.elm b/src/Sound.elm index 96bd6a1..dced634 100644 --- a/src/Sound.elm +++ b/src/Sound.elm @@ -14,14 +14,6 @@ type Sound } - -{- } - fromPath : String -> Sound - fromPath p = - S { path = p } --} - - noSound = S { path = "NO_SOUND", duration = 0, startPercent = 0, endPercent = 0 }