How can I iterate over a String containing non-ascii characters?

Hi all,

I am working on a function which treats a String as a series of Dictionary look-ups in a Dictionary which has Strings as keys and Events or Event-modifying Functions as values.

Here is a minimal version of it.

(
f = { |string, dict|
    var s, modIndices;
    s = string.copy;
    s = s.split($ );
    s = s.collect({ |item| item.separate });
    s = s.collect({ |beat|
        var numMods = beat.select({ |item| dict[item].isKindOf(Function) }).size;
        var actualBeatSubdiv = (beat.size - numMods).reciprocal·;
        beat.collect({ |item|
            case({
                dict[item].isKindOf(Event)
            }, {
                dict[item] ++ (dur: actualBeatSubdiv)
            }, {
                dict[item].isKindOf(Function)
            }, {
                dict[item]
            })
        });
    }).flatten(1);
    modIndices = s.selectIndices({ |item| item.isKindOf(Function) });
    modIndices.do({ |item|
        s[item - 1] = s[item].value(s[item - 1])
    });
    s.removeAllSuchThat({ |item| item.isKindOf(Function) });
    s;
}
);

(
d = Dictionary[
    "c" -> (note: 0),
    "#" -> { |ev| ev[\note] = ev[\note] + 1 }
]
);

f.("cc c#", d);
// -> [ ( 'note': 0, 'dur': 0.5 ), ( 'note': 0, 'dur': 0.5 ), ( 'note': 1, 'dur': 1.0 ) ]

This works well with the “#” character, but doesn’t work with “♭”, as the latter is non-ascii. As per the String help, non-ascii characters are composed of multiple Chars.

"#".size // -> 1
"♭".size // -> 3

As such, the following returns unwanted results. The nil’s and incorrect duration below are a by-product of the fact that “♭” is composed of multiple Chars.

e = Dictionary[ "b" -> (note: 11), "♭" -> { |ev| ev[\note] = ev[\note] - 1 } ];

f("bb b♭");
// -> -> [ ( 'note': 11, 'dur': 0.5 ), ( 'note': 11, 'dur': 0.5 ), ( 'note': 11, 'dur': 0.25 ), nil, nil, nil ]

Any thoughts on how I can use both ascii and non-ascii characters in my String-to-Events function?
Either I need to completely re-think my approach, or I need a method similar to String:separate which will take, e.g., “b♭” and return [ “b”, “♭” ].

Why not just use capital ‘F’ for flat? Or use one of the existing text to music formats, I think there is a lilypond and ABC quark.

You need to use proper utf8 methods for Unicode processing, I’m not familiar with supercollider’s support for this so can’t help further.

Iirc there is a quark called Strang that could help…

1 Like

Thanks for the reply. I often use “F” for the F an octave and a fourth above middle C, so its use for an accidental is out. An unspoken implication of your answer is that I can find some other ASCII character for “flat”, and it may come to that if no suitable way of working with non-ASCII characters can be found.

I haven’t explored lilypond much and I can have a look at that. My notation system is meant to be more flexible and open-ended than ABC, so that would not be a suitable replacement.

Thanks again for replying.

Iirc there is a quark called Strang that could help…

Indeed! The below works perfectly. Thanks!

"b♭".asStrang.array
1 Like

Lilypond requires a space between each note so ‘f’ and ‘s’ are used, the numbers are durations (4 = crotchet, 8 = semiquaver), dynamics and extra stuff are written ‘\p’ = piano, ‘<’ crescendo.

a4\p\< bf cs8 d8 e4\f
1 Like