gogins / csound-webserver-opcodes

The "webserver" opcodes enable Csound to act as a local Web server that can host HTML5 GUIs and score generators.
GNU Lesser General Public License v2.1
0 stars 0 forks source link

Fix variable and unacceptable latency and choking in the JSON-RPC stack #24

Closed gogins closed 2 years ago

gogins commented 2 years ago

This might be my problem:

Using streaming results in a smaller data overhead and server load than polling, because ideally the client makes only one HTTP request. Unfortunately, streaming creates issues under certain conditions because browsers and network intermediaries(like proxies) often try to handle the partial responses as broken pieces of one large HTTP response (which is normal HTTP behavior), instead of as the separate messages they were intended to be.

If in fact that is the problem and I can't fix it in the HTTP request, then I will have to re-implement the JSON-RPC stack using WebSockets.

This too seems relevant: https://stackoverflow.com/questions/69553798/fetch-request-vs-websocket-which-one-is-faster.

gogins commented 2 years ago

This may be an issue only on Chrome, see this.

But when I use Safari instead of Chrome, I get a warning that my page is using significant memory.

gogins commented 2 years ago

On the whole this is just behaving as though the HTTP connection is just getting overloaded. I'm not sure why or what to do about it or even if it's possible to do anything about it.

jasonhallen commented 2 years ago

I've noticed a latency issue that may be related to the JSON-RPC stack as well. I've got a button event listener in JavaScript that triggers csound.ScoreEvent(), and there's an inconsistent lag between when the button is pressed and when the note sounds from the Csound instrument. Sometimes the lag is almost short enough to be acceptable, but often the lag can be up to half a second. This is happening in Firefox.

gogins commented 2 years ago

Installing boost is not so bad, it's just untarring an archive. So I can proceed:

template<class Allocator>
bool
is_upgrade(http::header<true,
    http::basic_fields<Allocator>> const& req)
{
    if(req.version() < 11)
        return false;
    if(req.method() != http::verb::get)
        return false;
    if(! http::token_list{req[http::field::connection]}.exists("upgrade"))
        return false;
    if(! http::token_list{req[http::field::upgrade]}.exists("websocket"))
        return false;
    return true;
}
gogins commented 2 years ago

If I get tired of WebSockets and boost and what all, there is Open Sound Control.

gogins commented 2 years ago

This problem has nothing to do with network protocols. I began to re-implement with WebSockets and the same thing happened. But I noticed that if I did not actually call the Csound API csoundInputMessage, but only logged the received network requests, there were no JavaScript errors. I verified that this is the same for both WebSockets and fetch.

There is some problem with Csound calling the Csound API methods. There is probably a disguised data race with a timeout. If I can work around that, I can go back to fetch and things will be good. If I can't work around that, I'll go back to csound.node. Perhaps it's just a mutex that needs to be re-entrant.

gogins commented 2 years ago

Or I can enqueue calls until Csound is ready.

gogins commented 2 years ago

No valgrind on the Mac.

gogins commented 2 years ago

I suspect the problem is that Csound has a global API mutex that is recursive. If with these opcodes the global API mutex is being locked by different threads, the results are undefined. I will see if there is another way of getting score events into Csound in real time. If I need to, I can instrument Csound to identify the threads involved.

gogins commented 2 years ago

Using the "Async" versions of score functions helped. I think there is another problem in that the Lindenmayer system is stacking up notes, this appears to be the cause of the choking. At any rate I don't see any need for WebSockets now, though I have learned to program them.

gogins commented 2 years ago

I made Csound.ReadScore and Csound.InputMessage async only (i.e. using csoundReadScoreAsync and csoundInputMessageAsync). The issue was confounded by the side effects of an inefficient algorithm in my new p5js.csd example, which I was using as a test. I also replaced nlohmann's JSON library with Boost's JSON library.

All now seems well.

gogins commented 2 years ago

Re-opening to remove Boost dependencies and restore nlohmann JSON library. (Shudder) I was about to violate my rules about fixing what isn't broken, and keeping it simple, stupid.

gogins commented 2 years ago

All is now good.

jasonhallen commented 2 years ago

Thanks so much for working on this, Mike! I'm trying to test out the newest version now. The same CSD code which worked in the previous webserver version is now not generating a note event. Here's the code:

if (startButton.text === "START") {
                    startButton.text = "STOP";
                    (async () => {
                        csound.ScoreEvent('i', [2, 0, -1]);
                    })();
                    } else {
                    startButton.text = "START";
                    (async () => {
                        csound.ScoreEvent('i', [-2, 0, 1]);
                    })();
                    }
                }

I get the following Csound error message: insert_score_event(): invalid instrument number or name 0. Has something changed in how .ScoreEvent works?

Thanks! Jason

gogins commented 2 years ago

I'll debug this.

On Fri, Aug 5, 2022, 13:33 Jason Hallen @.***> wrote:

Thanks so much for working on this, Mike! I'm trying to test out the newest version now. The same CSD code which worked in the previous webserver version is now not generating a note event. Here's the code:

if (startButton.text === "START") { startButton.text = "STOP"; (async () => { csound.ScoreEvent('i', [2, 0, -1]); })(); } else { startButton.text = "START"; (async () => { csound.ScoreEvent('i', [-2, 0, 1]); })(); } }

I get the following Csound error message: insert_score_event(): invalid instrument number or name 0. Has something changed in how .ScoreEvent works?

Thanks! Jason

— Reply to this email directly, view it on GitHub https://github.com/gogins/csound-webserver-opcodes/issues/24#issuecomment-1206687494, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABQIGJJEOISK4ZLI5KZPHPDVXVGAJANCNFSM54H537YA . You are receiving this because you modified the open/close state.Message ID: @.***>

gogins commented 2 years ago

I'm reopening this issue, as there is definitely a bug, but I think it is a Csound bug. In the POST handler in my C++ code for csound.ScoreEvent:

// The following line works if the POST handler invokes csoundScoreEvent. But then latency is variable, unpredictable, and often unusable.
// But it does NOT work if the POST handler invokes csoundScoreEventAsync.
csound.ScoreEvent('i', [ 1.0000,    0.0000,  512.0000, 6000.0000,    6.0000,    1.0500 ]);
// The following line works (it invokes csoundInputMessageAsync). Latency seems fine.
//csound.InputMessage("i 1.0000    0.0000  512.0000 6000.0000    6.0000    1.0500\\n");

This strongly implies that there is a bug in Csound's csoundScoreEventAsync function, which we need if we don't want crazy latency.

If you need your stuff to work right now, switch to creating string notes and calling csound.InputMessage instead of csound.ScoreEvent. But I will continue to debug this.

gogins commented 2 years ago

I am closing this issue with a note that using Csound.ReadScore or Csound.InputMessage seems to be a good workaround for Csound.ScoreEvent. The overhead of converting to and from text is usually negligible in this context.

If there are still problems, or if you really need Csound.ScoreEvent, then please re-open this issue.