• ddw_music

    @impression said:

    • for what are you using text search parsing->moses->1000 for? I just noticed it turns voice number 1 into a zero, but don`t really get how and why.

    [poly] assigns an index to note numbers. You'd think this would be easy to use directly as a row number in the [text], but [text] treats high row indices as "append" rather than "put at this exact row," so you don't have a guarantee that poly index 3 maps onto text row 2 (poly counts from 1, not 0).

    So to find the row index to use, there are two cases:

    • Search for the poly index and find it -- the [text search] result is >= 0. In that case we just want to pass the row index through -- that's the right branch of moses.
    • Or, [text search] didn't find it. Then its result is -1. That's not a valid row index for [text put], so, we have to replace it with a high number, to tell [text put] to append. So moses --> "1000" means simply, replace negative numbers with 1000 but pass 0 or positive numbers through.
    • also I don't fully understand "pd notecount", even you explained already what it does, it lets my brain explode:smile:
      You count up + 1 once a note is pressed and count down -1 once its released. I don`t know what's the idea of "max 0" feeding itself back into + though.

    A negative note count is invalid, but could happen if, for instance, you started the patch running while holding keys on the MIDI keyboard. The max 0 just prevents the count from being negative.

    It all works as you showed, but when I set list split to 3 it basically freezes PD. Do you know by any chance why?
    I would need to split my list into 3 later on.

    It's very important, when using [until] for looping, to use either a fixed count of loop iterations or to make sure that the stop "bang" signal goes into the right inlet. If it's freezing, probably that's not happening.

    hjh

    posted in technical issues read more
  • ddw_music

    @whale-av said:

    But fortunately, [raindrops] is included in the tarball of all files here....... http://aspress.co.uk/sd/ so it is an omission mistake on the page you linked to....

    Ah ok ... thanks.

    Yeah, it's an interesting book but sometimes hard to see what is what. This [raindrops] abstraction is a parameterized version of another figure in the book, but without a clear explanation in the text AFAICS.

    Thanks for the tip --

    hjh

    posted in technical issues read more
  • ddw_music

    Sorry for what is perhaps a rather specific question, but... does anyone else have the Andy Farnell Designing Sound book?

    I never did that much with it because there's a fundamental editing mistake (IMO) -- many patches refer to abstractions, but there is usually no explicit link between the name of the abstraction and the figure in which the abstraction is defined.

    Case in point (and relevant to a project) -- figure 38.7 (page 445) uses an abstraction [raindrops 20 0.1].

    http://aspress.co.uk/sd/practical15.html doesn't have it.

    Actually I'm looking through that chapter for any abstraction that has only $1 and $2 but not $3 or higher (bc the use of the abstraction provides only 2 arguments) and I don't see one -- so maybe it was just omitted?

    Just wondering if anyone had run into this and found a solution.

    hjh

    posted in technical issues read more
  • ddw_music

    @impression said:

    Omg, never in my life would I have figured that out

    Around 2019 or 2020, I might have said the same thing.

    I'm more accustomed to working with the high-level language, SuperCollider. In this case, it's easier to write this logic in SC because, using SC arrays where pack/unpack is used in the Pd patch, you can access any element at any time, and pack a new array just by writing [item, otherItem, thirdItem, etc.]. In SC, most of the spaghetti in the orange part could be written with a single line: [list[2], SystemClock.seconds, list[1], list[0]].

    The orange bit is really only reordering the input list, and adding an item for the current time. (Why did it have to be ordered with velocity first? Because that's the only way to get [route] to split note-on and note-off.) It looks massively complex, but it's really not doing anything more than that. The complexity arises because data flow out of [unpack] in a different order than they need to go into [pack] -- the [unpack]'s right outlet needs to be withheld, so that [pack]'s hot inlet at left is not triggered until all the other inlets have been populated.

    There are plenty of tricks for that (e.g., the use of [f] on the orange side). It took me a few years to learn them. You can learn them too.

    hjh

    posted in technical issues read more
  • ddw_music

    @impression I think it's not possible to avoid the central problem, which is associating a note-off message with the prior matching note-on message. Unfortunately, this problem takes some careful thinking. It's tempting to avoid doing this careful thinking at the incoming-message stage and try to push the problem off to another part of the patch. But, I think everything is going to be easier if the note parsing is done correctly in the first place.

    I think you will need one [text] just to parse the messages into chords, and then another [text] as a chord repository.

    The basic flow I'm thinking of is:

    • Use [poly] to assign an index to incoming note numbers. (Why? Because, when a note-off message comes in, you have to find the matching note-on record and look up the time. Then you can use that time to calculate the duration. There is no way to do this without matching note-on and note-off, and [poly] does this for you.)

    • Note on: Some jiggery-pokery to rearrange the note-on data into the format "poly-index timer-value note-num velocity" and then write this list into a [text] at the row number where that index lives.

    • Note off: Find the [text] entry matching index, unpack it, subtract the current time from the message's time (to get duration), pack into the desired format "note-num vel duration' and add to the current chord.

    I had to make a decision about how to know when the current chord is finished. In this patch, it's when all notes have been released -- [pd notecount] gives a bang when a note-off decrements the active note count down to 0. That may or may not be right for your use case, but that part can probably be adjusted without requiring too much surgery on the rest.

    In any case, in the end I get messages such as the following:

    chord: 69 101 394.739 62 101 394.739 60 101 394.739 64 101 394.739 67 101 394.739 71 101 394.739
    chord: 65 101 394.739 72 101 417.959 67 101 394.739 69 101 394.739
    chord: 71 101 394.739 65 101 394.739 60 101 394.739 62 101 394.739 64 101 394.739 72 101 394.739
    chord: 65 101 394.739 64 101 394.739 67 101 417.959 72 101 394.738 71 101 394.739

    ... which could be read out in groups of three for playback. (The durations and velocities are all the same because I was testing with MIDI notes from SuperCollider -- I had specified a duration of 400 ms for each note and Pd is pretty close to that. So I think that confirms the approach.)

    (Note that it's a good idea to reset the [timer] at the beginning of a run -- I set up MIDI controller 37 as a trigger for that but you could do it any other way you want.)

    hjh

    pd-midi-notes.png

    midi-parsing.pd

    posted in technical issues read more
  • ddw_music

    @impression What if you store your chords in the format:

    note0 vel0 dur0 note1 vel1 dur1 ...

    That is, don't bother with clumping all the note numbers together at all -- interleave them from the start.

    Then your iterator can "get $1 3" and increment by 3 each time.

    Maybe an example later when I have a bit more time.

    hjh

    posted in technical issues read more
  • ddw_music

    @impression said:

    I can’t really use [clone] for my synth setup.

    I'm curious, why is that? There's probably a solution. If it's the first time you're using [clone], I can see how it might be tricky to figure out, but "haven't figured it out" may not be the same as "can't use it."

    I’d basically need to generate my own note on and note off messages manually.

    That's probably the [makenote] object.

    hjh

    posted in technical issues read more
  • ddw_music

    For computer-to-computer communication, Open Sound Control over a LAN may be easier than MIDI (as a transport device).

    hjh

    posted in technical issues read more
  • ddw_music

    BTW though: I hadn't looked closely at the sources earlier, so I was conjecturing. Looked just now. If we take, say, "unpack" as an example of an object that typically has multiple outlets, and see how it responds to a list:

    in m.connective.c:

    static void unpack_list(t_unpack *x, t_symbol *s, int argc, t_atom *argv)
    {
        t_atom *ap;
        t_unpackout *u;
        int i;
        if (argc > x->x_n) argc = (int)x->x_n;
        for (i = argc, u = x->x_vec + i, ap = argv + i; u--, ap--, i--;)
        {
            t_atomtype type = u->u_type;
            if (type != ap->a_type)
                pd_error(x, "unpack: type mismatch");
            else if (type == A_FLOAT)
                outlet_float(u->u_outlet, ap->a_w.w_float);
            else if (type == A_SYMBOL)
                outlet_symbol(u->u_outlet, ap->a_w.w_symbol);
            else outlet_pointer(u->u_outlet, ap->a_w.w_gpointer);
        }
    }
    

    ... where each output value calls either outlet_float() or outlet_symbol() or outlet_pointer(). It'll move to the next outlet when the outlet_ function returns.

    m_obj.c:

    void outlet_float(t_outlet *x, t_float f)
    {
        t_outconnect *oc;
        if(!stackcount_add())
            outlet_stackerror(x);
        else
            for (oc = x->o_connections; oc; oc = oc->oc_next)
                pd_float(oc->oc_to, f);
        stackcount_release();
    }
    

    OK, check for stack overflow and then call pd_float() for each connection from that outlet. (_bang, _symbol, _pointer are similar.)

    m_pd.c:

    void pd_float(t_pd *x, t_float f)
    {
        if (x == &pd_objectmaker)
            ((t_floatmethodr)(*(*x)->c_floatmethod))(x, f);
        else
            (*(*x)->c_floatmethod)(x, f);
    }
    

    Get a reference to the next object's floatmethod function, e.g. and call it: a method handler --> an iterator over connections --> a connection handler --> another object's method handler, at which point the cycle recurs.

    So the way that it backtracks seems to be simply that e.g. outlet_float() returns and the method handler resumes where it left off: normal tree-traversal recursion using C's call stack (without data return, as these are all void).

    Gotos do appear in the Pd source but it looks like the vast majority of these are used for early-exit or for input validation before performing some operation -- goto error, goto fail, goto doit, etc. I don't see them in the message-passing functions. (What I also don't see in the message-passing functions is an explicit stack of objects/connections -- stackcount_add() is just a global counter, no real push/pop -- so if I guessed that pd keeps its own stack of objects visited, that was a wrong conjecture.)

    hjh

    posted in technical issues read more
  • ddw_music

    @impression said:

    Lets say I want 32 polyphony, that would be an insane effort.

    IMO this all comes down to data structure design. A bad design will require insane effort. A good design will scale up to any amount of polyphony you need.

    What's the input? From your MIDI keyboard?

    What do you need to do with the notes afterward? Just spit the MIDI events back out, or quantize them, etc etc?

    [text sequence] is a nice way to store note sequences. Each entry starts with the time delta before this note, followed by the note data, which may be anything you want (e.g., note number, sustain time, velocity, suitable for [makenote] when playing back).

    It would really help if you could explain the goal, to help design a way to un-insane the insane effort.

    hjh

    posted in technical issues read more

Internal error.

Oops! Looks like something went wrong!