• sidetrack

    From luserdroog@21:1/5 to All on Tue Dec 7 18:42:07 2021
    I'm making some progress at reconceiving my pianoroll app
    to be a more coherent object based design. But also kinda
    not making progress on it. I doodled up a sketch of the overall
    structure of the MVC example but instead of working on that,
    here's a "robust" function for decoding note names from letters
    and producing a MIDI note number.

    It uses two minor tetrachords to construct the intervals of an
    aeolian scale then takes a running sum to get "fretstops" for
    each of the scale degrees. Then you index the resulting array
    with 0 for 'a', 1 for 'b' etc, a little more twiddling and poof kazow
    presto! The desired number comes out the other end.

    Is there a better way to do the running_sum()? I was trying to
    do an APL "scan" and doing a reduce() while building the result
    backwards was the best I could come up with.

    const major_tetrachord = [ 2, 2, 1 ];
    const mixolydian_mode = [ ...major_tetrachord,
    ...major_tetrachord,
    2 ];
    const minor_tetrachord = [ 2, 1, 2 ];
    const aeolian_mode = [ ...minor_tetrachord,
    ...minor_tetrachord ];
    const scale = [ ...mixolydian_mode,
    ...mixolydian_mode,
    ...major_tetrachord ];

    function running_sum( a ){
    return a.reduce( (pre,cur)=>[ cur+pre[0], ...pre ], [0] ).reverse();
    }
    console.log(running_sum(aeolian_mode));
    console.log(running_sum(scale));

    function note( str, octave=0 ){
    var num = str.toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0);
    var fromA = running_sum(aeolian_mode)[ num ] +
    (str.length > 1 ? (str[1]=='#' ? 1 : -1)
    : 0);
    var fromC = fromA + 9;
    return octave*12 + (fromC >= 12 ? fromC - 12 : fromC);
    }

    console.log(note("a",0));
    console.log(note("c#",0));
    console.log(note("eb",0));

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael Haufe (TNO)@21:1/5 to luser...@gmail.com on Wed Dec 8 13:03:37 2021
    On Tuesday, December 7, 2021 at 8:42:11 PM UTC-6, luser...@gmail.com wrote:

    Is there a better way to do the running_sum()? I was trying to
    do an APL "scan" and doing a reduce() while building the result
    backwards was the best I could come up with.

    A `reduceRight` method exists on arrays:

    <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From luserdroog@21:1/5 to luserdroog on Wed Dec 8 16:23:04 2021
    On Wednesday, December 8, 2021 at 6:17:07 PM UTC-6, luserdroog wrote:

    function running_sum( a ){
    var t = [];
    a.reduce( (pre,cur)=>{t.push(pre+cur),pre+cur}

    , 0

    );
    return t;
    }

    It's not a "one liner", but this is probably a bit more efficient AFAICT.
    In APL, it's just `+\`.

    Need to use initial value zero (or prepend the original first element).
    I really think that's right, now.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From luserdroog@21:1/5 to All on Wed Dec 8 16:17:01 2021
    On Wednesday, December 8, 2021 at 3:03:49 PM UTC-6, Michael Haufe (TNO) wrote:
    On Tuesday, December 7, 2021 at 8:42:11 PM UTC-6, luser...@gmail.com wrote:

    Is there a better way to do the running_sum()? I was trying to
    do an APL "scan" and doing a reduce() while building the result
    backwards was the best I could come up with.
    A `reduceRight` method exists on arrays:

    <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight>

    Hmm. The parameter names make that seem like the right choice. At least it gives
    you an `accumulator`. But on closer inspection, reverse()'s `previousValue` behaves
    exactly the same, and might have been (better IMO) called *accumulator*, too.

    I could do it without reversing by just pushing each intermediate result (that's all
    an APL scan is: an array of the intermediate results from a reduce operation, probably
    ought to have defined it more precisely in OP).

    function running_sum( a ){
    var t = [];
    a.reduce( (pre,cur)=>{t.push(pre+cur),pre+cur} );
    return t;
    }

    It's not a "one liner", but this is probably a bit more efficient AFAICT.
    In APL, it's just `+\`.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael Haufe (TNO)@21:1/5 to luser...@gmail.com on Wed Dec 8 21:38:26 2021
    On Wednesday, December 8, 2021 at 6:23:12 PM UTC-6, luser...@gmail.com wrote:
    On Wednesday, December 8, 2021 at 6:17:07 PM UTC-6, luserdroog wrote:

    function running_sum( a ){
    var t = [];
    a.reduce( (pre,cur)=>{t.push(pre+cur),pre+cur}
    , 0
    );
    return t;
    }

    It's not a "one liner", but this is probably a bit more efficient AFAICT. In APL, it's just `+\`.
    Need to use initial value zero (or prepend the original first element).
    I really think that's right, now.

    functionally:

    const add = (left, right) => left + right

    const sum = (xs) => xs.reduce(add,0)

    sum([1,2,3,4,5]) // 15

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From luserdroog@21:1/5 to All on Thu Dec 9 17:39:17 2021
    On Wednesday, December 8, 2021 at 11:38:30 PM UTC-6, Michael Haufe (TNO) wrote:
    On Wednesday, December 8, 2021 at 6:23:12 PM UTC-6, luser...@gmail.com wrote:
    On Wednesday, December 8, 2021 at 6:17:07 PM UTC-6, luserdroog wrote:

    function running_sum( a ){
    var t = [];
    a.reduce( (pre,cur)=>{t.push(pre+cur),pre+cur}
    , 0
    );
    return t;
    }

    It's not a "one liner", but this is probably a bit more efficient AFAICT. In APL, it's just `+\`.
    Need to use initial value zero (or prepend the original first element).
    I really think that's right, now.
    functionally:

    const add = (left, right) => left + right

    const sum = (xs) => xs.reduce(add,0)

    sum([1,2,3,4,5]) // 15

    That does an APL reduce (coincidentally named) which looks like:

    +/

    But I'm after the subtly different function does a "scan" or piece-wise
    reduce. In APL, it's almost imperceptibly different:

    +\

    (There's a joke that APL\360 was named with the backslash character
    in there so the product name means "APL extends 360" if treated as
    an APL program. \ means "extend" if the left and right are values, but
    in the snippets under consideration \ means "scan" because the left
    (+) is a function and not a value. I will endeavor to stop talking about
    APL after this message.)

    To be more functional, I think it would look like this:

    Array.prototype.scan = function( f ){
    return this.map( (e,i,a)=>a.slice(0,i+1).reduce(f,0) );
    }
    const add = (left,right) => left + right

    function running_sum( a ){
    return [ 0, ...a.scan( add )];
    }

    (I forgot I also needed the result of running_sum() to start with a zero,
    in addition to the zero that gets fed to reduce() to ensure the first
    element of the input array shows up in the output array.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From luserdroog@21:1/5 to luserdroog on Thu Dec 9 17:44:34 2021
    On Thursday, December 9, 2021 at 7:39:22 PM UTC-6, luserdroog wrote:
    On Wednesday, December 8, 2021 at 11:38:30 PM UTC-6, Michael Haufe (TNO) wrote:
    On Wednesday, December 8, 2021 at 6:23:12 PM UTC-6, luser...@gmail.com wrote:
    On Wednesday, December 8, 2021 at 6:17:07 PM UTC-6, luserdroog wrote:

    function running_sum( a ){
    var t = [];
    a.reduce( (pre,cur)=>{t.push(pre+cur),pre+cur}
    , 0
    );
    return t;
    }

    It's not a "one liner", but this is probably a bit more efficient AFAICT.
    In APL, it's just `+\`.
    Need to use initial value zero (or prepend the original first element).
    I really think that's right, now.
    functionally:

    const add = (left, right) => left + right

    const sum = (xs) => xs.reduce(add,0)

    sum([1,2,3,4,5]) // 15
    That does an APL reduce (coincidentally named) which looks like:

    +/

    But I'm after the subtly different function does a "scan" or piece-wise reduce. In APL, it's almost imperceptibly different:

    +\

    (There's a joke that APL\360 was named with the backslash character
    in there so the product name means "APL extends 360" if treated as
    an APL program. \ means "extend" if the left and right are values, but
    in the snippets under consideration \ means "scan" because the left
    (+) is a function and not a value. I will endeavor to stop talking about
    APL after this message.)

    To be more functional, I think it would look like this:

    Array.prototype.scan = function( f ){
    return this.map( (e,i,a)=>a.slice(0,i+1).reduce(f,0) );

    dangitall.

    }
    const add = (left,right) => left + right

    function running_sum( a ){
    return [ 0, ...a.scan( add )];
    }

    (I forgot I also needed the result of running_sum() to start with a zero,
    in addition to the zero that gets fed to reduce() to ensure the first
    element of the input array shows up in the output array.)

    Erhm. We don't need that zero in reduce() anymore. The map() takes
    care of that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From luserdroog@21:1/5 to luserdroog on Thu Dec 9 19:22:56 2021
    On Tuesday, December 7, 2021 at 8:42:11 PM UTC-6, luserdroog wrote:
    I'm making some progress at reconceiving my pianoroll app
    to be a more coherent object based design. But also kinda
    not making progress on it. I doodled up a sketch of the overall
    structure of the MVC example but instead of working on that,
    here's a "robust" function for decoding note names from letters
    and producing a MIDI note number.

    It uses two minor tetrachords to construct the intervals of an
    aeolian scale then takes a running sum to get "fretstops" for
    each of the scale degrees. Then you index the resulting array
    with 0 for 'a', 1 for 'b' etc, a little more twiddling and poof kazow
    presto! The desired number comes out the other end.

    Revised and expanded code from OP. It can now produce an array
    of midi note numbers given a chord name (like "Bbm" or "F#7").
    Some explanation of how I'm using the scale and the chord template
    is in this post:

    https://music.stackexchange.com/a/6181/1344

    Adding Major 7th chords would require some reworking like starting
    with a different mode. Another nice extension would be an option
    for closed vs. open voicing or a spectrum or palette of voicings.



    const major_tetrachord = [ 2, 2, 1 ];
    const minor_tetrachord = [ 2, 1, 2 ];

    //mode names according to Boethius
    const ionian_mode = [ ...major_tetrachord, //defined by Nicomachus as two disjunct
    2, //tetrachords separated by a tone
    ...major_tetrachord ];

    const mixolydian_mode = [ ...major_tetrachord, //need this for 7th chords and stuff
    ...major_tetrachord, //like A/E and C/G chords
    2 ];

    const aeolian_mode = [ ...minor_tetrachord, //defined by Nicomachus as two conjoint
    ...minor_tetrachord ]; //tetrachords

    const long_mix_scale = [ ...mixolydian_mode,
    ...mixolydian_mode,
    ...major_tetrachord ];

    const long_scale = ( scale )=>[ ...scale, ...scale, ...scale ];

    Array.prototype.scan = function( f ){
    return this.map( (e,i,a)=>a.slice(0,i+1).reduce(f) );
    }
    Array.prototype.scan_v2 = function( f ){
    var r = [];
    this.reduce( (left,right)=>{ var t = f(left,right); r.push(t); return t; }, 0 );
    return r;
    }
    const add = (left,right) => left + right
    const running_sum = ( a ) => [ 0, ...a.scan( add ) ]

    console.log(running_sum(aeolian_mode)); console.log(running_sum(long_mix_scale)); console.log(running_sum(long_scale(mixolydian_mode)));

    function note( str, octave=0 ){
    var num = str.toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0);
    var fromA = running_sum(aeolian_mode)[ num ] +
    (str.length > 1 ? (str[1]=='#' ? 1 : -1)
    : 0);
    var fromC = fromA + 9;
    return octave*12 + (fromC >= 12 ? fromC - 12 : fromC);
    }

    function chord( str, octave=0 ){
    var off = 0;
    var tweak = 0;
    var is7th = 0;
    var twiddle = -3; //+9 (midi number of 'A') -12 (lower octave)

    var num = str.toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0);
    var base = running_sum(aeolian_mode)[ num ];
    str = str.slice(1);

    if( str.length > 0 ){
    off = str[0]=='#' ? 1
    : str[0]=='b' ? -1
    : 0;
    if( off ){ str = str.slice(1); }
    }

    if( str.length > 0 && str[0]=='m' ){
    tweak = -1;
    str = str.slice(1);
    }

    if( str.length > 0 && str[0]=='7' ){
    is7th = 1;
    }

    var template = is7th ? [ 1,5,7,8,10,12,14,15 ] //figured bass for chord shape
    : [ 1,5, 8,10,12, 15 ];
    var mode = [ ...mixolydian_mode ];
    mode[1] += tweak; //minor chord uses a flat 3rd
    mode[2] -= tweak; //balance the adjacent interval
    //console.log( mode );
    //console.log( running_sum( mode ) );

    var scale = running_sum( long_scale( mode ) );
    //console.log( scale );

    var chord = template.map( x=> scale[x-1] + base + off + twiddle );
    chord = chord.map( x=> x+octave*12 );
    chord = chord.filter( x=> x>=0 );
    return chord;
    }

    console.log(note("a",0));
    console.log(note("c#",0));
    console.log(note("eb",0));
    console.log(chord("a"));
    console.log(chord("am"));
    console.log(chord("a7"));
    console.log(chord("ebm7",4));


    Output:
    9
    1
    3
    Array(5) [ 4, 9, 13, 16, 21 ]
    Array(5) [ 4, 9, 12, 16, 21 ]
    Array(7) [ 4, 7, 9, 13, 16, 19, 21 ]
    Array(8) [ 51, 58, 61, 63, 66, 70, 73, 75 ]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Elhwen Dico@21:1/5 to All on Fri Dec 10 16:30:01 2021
    function running_sum(a: number[]) {
    return a.reduce(
    (acc: number, current: number) => acc + current,
    0);
    }

    console.log(running_sum([1, 2, 3]));
    console.log(running_sum([]));

    Le 09/12/2021 à 01:23, luserdroog a écrit :
    On Wednesday, December 8, 2021 at 6:17:07 PM UTC-6, luserdroog wrote:

    function running_sum( a ){
    var t = [];
    a.reduce( (pre,cur)=>{t.push(pre+cur),pre+cur}

    , 0

    );
    return t;
    }

    It's not a "one liner", but this is probably a bit more efficient AFAICT.
    In APL, it's just `+\`.

    Need to use initial value zero (or prepend the original first element).
    I really think that's right, now.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Sauyet@21:1/5 to luser on Fri Dec 10 07:26:43 2021
    luser wrote:

    But I'm after the subtly different function does a "scan" or piece-wise reduce. In APL, it's almost imperceptibly different:
    [ ...]
    To be more functional, I think it would look like this:

    Array.prototype.scan = function( f ){
    return this.map( (e,i,a)=>a.slice(0,i+1).reduce(f,0) );
    }
    const add = (left,right) => left + right

    function running_sum( a ){
    return [ 0, ...a.scan( add )];
    }

    I find this much cleaner:

    const scan = (fn) => (init) => (xs) =>
    xs .reduce ((ys, x) => ((ys .push (fn (ys .at (-1), x))), ys), [init])

    const running_sum = scan ((a, b) => a + b) (0)

    running_sum ([1, 2, 3, 4, 5]) //=> [0, 1, 3, 6, 10, 15]


    While this is more elegant:

    const scan = (fn) => (init) => (xs) =>
    xs .reduce ((ys, x) => [...ys, fn (ys .at (-1), x)], [init])

    It will almost certainly be less efficient as it has to build a new
    array on each iteration.

    If `Array.prototype.at` is not available in your environment, it's
    trivial enough to write your own `last` function and replace
    `ys .at (-1)` with `last (ys)`.


    -- Scott

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Elhwen Dico@21:1/5 to All on Fri Dec 10 16:37:02 2021
    const running_sum = (a) => a.reduce((acc, curr) => acc + curr, 0);

    console.log(running_sum([1, 2, 3]));
    console.log(running_sum([]));


    Le 10/12/2021 à 16:30, Elhwen Dico a écrit :

    function running_sum(a: number[]) {
        return a.reduce(
            (acc: number, current: number) => acc + current,
            0);
    }

    console.log(running_sum([1, 2, 3]));
    console.log(running_sum([]));

    Le 09/12/2021 à 01:23, luserdroog a écrit :
    On Wednesday, December 8, 2021 at 6:17:07 PM UTC-6, luserdroog wrote:

    function running_sum( a ){
    var t = [];
    a.reduce( (pre,cur)=>{t.push(pre+cur),pre+cur}

       , 0

    );
    return t;
    }

    It's not a "one liner", but this is probably a bit more efficient
    AFAICT.
    In APL, it's just `+\`.

    Need to use initial value zero (or prepend the original first element).
    I really think that's right, now.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Thomas 'PointedEars' Lahn@21:1/5 to Elhwen Dico on Sat Dec 11 01:56:11 2021
    Elhwen Dico wrote:

    [top post]

    Bats are supposed to post next door.

    --
    PointedEars
    FAQ: <http://PointedEars.de/faq> | <http://PointedEars.de/es-matrix> <https://github.com/PointedEars> | <http://PointedEars.de/wsvn/>
    Twitter: @PointedEars2 | Please do not cc me./Bitte keine Kopien per E-Mail.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Julio Di Egidio@21:1/5 to Thomas 'PointedEars' Lahn on Sat Dec 11 10:12:25 2021
    On 11/12/2021 01:56, Thomas 'PointedEars' Lahn wrote:
    Elhwen Dico wrote:

    [top post]

    Bats are supposed to post next door.

    Since of course he has posted the best code so far in this thread...

    And that's all you do, you incompetent spamming retarded piece of shit.

    *Troll Alert*

    Julio

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)