 # Help with a fudgeToScale function

I've been playing around with a function that will take a list of non-scale tones and use `fix` to lower/raise their `up` value one semitone so that they conform with a diatonic scale, e.g.

``````fix (|- up 1)  "[1,3,6,8,10]" -- notes in range conform to [0,2,4,5,7,9,11]
``````

Of course, I want this function to work across other octaves, for which I can provide it with a giant list of values to avoid (this works well and is tons of fun):

``````fix (|- up 1) "[-35,-33,-30,-28,-26,-23,-21,-18,-16,-14,-11,-9,-6,-4,-2,1,3,6,8,10,13,15,18,20,22,25,27,30,32,34,37,39,42,44,46]"
``````

And I can programatically generate this list from a list of scale tones:

``````let showableAvoidList scaleIntervals =  show completeAvoidList where
completeAvoidList = concat \$ transposeScale avoidIntervals <\$> [(-3)..3]
transposeScale avoidIntervals oct = (+ (oct * 12)) <\$> avoidIntervals
avoidIntervals  = filter (\n -> notElem n scaleIntervals) [0..11]
``````

So that

`showableAvoidList [0,2,4,5,7,9,11]` creates the giant list above.

But I thought that with `parseBP_E`, I’d be able to use this result directly with `fix`, e.g.

``````let fudgeToScale scaleIntervals = fix (|- up 1) (parseBP_E \$ showableAvoidList scaleIntervals) where
showableAvoidList scaleIntervals =  show completeAvoidList
completeAvoidList = concat \$ transposeScale avoidIntervals <\$> [(-3)..3]
transposeScale avoidIntervals oct = (+ (oct * 12)) <\$> avoidIntervals
avoidIntervals  = filter (\n -> notElem n scaleIntervals) [0..11]
``````

but I don’t seem to have all the pieces there yet. Could someone help me with this?

Also, I have no idea how inefficient this might be. I would love to hear if there is a more efficient/idiomatic way to do this (e.g., can this be done with modulo? That was also a dead end for me)

Finally figured this out, adding the solution here for completeness' sake:

``````let fudgeToScale scaleIntervals = fix (|- up 1) (up \$ parseBP_E \$ completeAvoidList scaleIntervals) where
completeAvoidList scaleIntervals = show \$ concat \$ transposeScale avoidIntervals <\$> [(-3)..3]
transposeScale avoidIntervals oct = (+ (oct * 12)) <\$> avoidIntervals
avoidIntervals  = filter (\n -> notElem n scaleIntervals) [0..11]
``````

It's pretty handy. With it you can do transformations on pitch (outside of the `Pattern Int` argument to `scale` where functions like `jux` won't work) and still confine notes to a set of diatonic intervals, e.g., using `jux` to harmonize with diatonic triads:

``````let dorian = [0,2,3,5,7,9,10]

d1  \$ fudgeToScale dorian
\$ juxBy 0.5 ((|+ up "[0,4,-5]") . (# s  "superpiano"))
\$ up "<[0 [2 3] 5 7] [4 <2 -2>]>" # s "superhammond"
``````

NB: It only works with diatonic scales, i.e., where any wrong note is at most one semitone above a correct note. Applying the function twice should work if there are bigger gaps in the pitch set.

1 Like

This is a very cool idea - I need to have a play this week, thanks for posting the solution Side note, I'm going to go with the name `fixToScale` instead I think -

Next thing I'm thinking about, is how can you make use of the pre-defined scales in `scaleList`?

ie `dorian` is already defined as a scale in `scaleList` so it should be possible to not have to redefine it... I think I need to learn more about types if I had a snowball chance in hell of implementing it `fixToScale` does make so much more sense!

About using predefined scales here, crucially the missing piece for me is being able to extract the scale intervals from a named scale. My original idea posted here was to undo the `scale` function (rounding down or something to any values that didn't fit), applying some transformation, then re-applying the `scale`. If I could figure out how to get access to the scale intervals, I probably could have done that. But my Haskell skills are worse now than they were a year ago.

The problem with using named scales in `fixToScale` is that while it works nicely with diatonic scales (where there is at most a major second between intervals), if there were a minor third between adjacent steps, the function would need to be applied twice; for a major third, three times etc.

I still think undo scale -> apply function -> redo scale is the best way to go for this kind of patterning - if we can figure out how to get the interval list from a scale.

1 Like