Stumped by something simple

Hello. This is my first post here.

I am trying to learn to use the oscP5 library, and I’m making a tester sketch based on one of the examples.

The attached code is supposed to send and receive osc messages, and help me build understanding of forming and parsing them by drawing a bigger dot on screen where I press the mouse button, sending those screen coordinates (in normalized float x, y, form) out on the osc server. When it receives the message, it draws a smaller dot at the same location. My plan was to start testing multiple concurrent sketches to find out for myself how osc messages can reach or not reach their intended goals when there are many sources and many receivers.

I got stumped because this sketch won’t draw the second smaller dot on a successfully received message. I know it succeeds because my console message appears correctly. I know the smalDot method works because I can invoke it from other places in the code and get the expected result.

I don’t know what is going on… and I am a pretty novice coder.

Can anyone point out my error?

Thanks

// osc send & receive tester 
import oscP5.*;
import netP5.*;

OscP5 oscP5;
NetAddress myRemoteLocation;

void setup() {
  size(400,400);
  frameRate(25);
  background(40);
  noStroke();

  oscP5 = new OscP5(this,12000);
  
  myRemoteLocation = new NetAddress("127.0.0.1",12000);
}

void draw() {
}

void bigDot (float x, float y) {
  fill(255,100);
  ellipseMode(CENTER);
  ellipse(x*width,y*height,50,50);
  println("bigDot   "+x+" "+y);
}

void smallDot (float x, float y) {
  fill(255,100);
  ellipseMode(CENTER);
  ellipse(x*width,y*height,30,30);
  println("smallDot "+x+" "+y);
}

void mousePressed() {
  println("---");
  OscMessage myMessage = new OscMessage("/test");
  float x = float(mouseX)/width;
  float y = float(mouseY)/height;
  myMessage.add(x); /* add a float to the osc message */
  myMessage.add(y); /* add a float to the osc message */
  bigDot(x,y);
  oscP5.send(myMessage, myRemoteLocation); 
}

void oscEvent(OscMessage theOscMessage) {
  if(theOscMessage.checkAddrPattern("/test")==true) {
    if(theOscMessage.checkTypetag("ff")) {
      float x = theOscMessage.get(0).floatValue();  
      float y = theOscMessage.get(1).floatValue();
      smallDot(x,y);
      return;
    }  
  }
  println("received unrecognized osc message");
}
2 Likes

Can you give us an example of the console output you are seeing with big and small dots?

Yes. Here’s the console output, and a screen shot. My println() function shows me that the call to smallDot() has been reached, but none appears.

I made a workaround in which I set a global boolean to ‘true’ when the smallDot() is needed, and in the main draw() loop I check that variable, make the dot (with a likewise globally stored x and y coordinate), and it works as hoped. But that leads me to a further problem in understanding OSC, which I’ll make a separate post on once I resolve this strange situation.

Thanks-

Display 2 does not exist, using the default display instead.
Display 1 is sun.awt.CGraphicsDevice@4f7d0008
OscP5 0.9.9 infos, comments, questions at http://www.sojamo.de/oscP5


### [2019/4/13 9:10:22] PROCESS @ OscP5 stopped.
### [2019/4/13 9:10:22] PROCESS @ UdpClient.openSocket udp socket initialized.
### [2019/4/13 9:10:23] PROCESS @ UdpServer.start() new Unicast DatagramSocket created @ port 12000
### [2019/4/13 9:10:23] PROCESS @ UdpServer.run() UdpServer is running @ 12000
### [2019/4/13 9:10:24] INFO @ OscP5 is running. you (10.0.1.7) are listening @ port 12000
---
bigDot   0.3975 0.3125
smallDot 0.3975 0.3125
---
bigDot   0.54 0.4725
smallDot 0.54 0.4725
---
bigDot   0.38 0.5825
smallDot 0.38 0.5825
Finished.
[Finished in 38.8s]

2 Likes

I believe the issue is that the event

void oscEvent(OscMessage theOscMessage)

is handled in a different thread than the main thread and drawing on your screen only works from within the main thread.

The topic of threads is quite a complex one, but to give you a short explanation:
To receive messages over the network (even on the same pc, using osc is considered using the network) your program has to actively wait for messages to arrive. If your program just waited for network messages it would not be able to do anything else during that time. To change that what happens is that the code waiting for network messages is executed in a little ‘sub program’ (a new thread) running in the background.
For reasons beyond the scope of this explanation any code running in the background is not allowed to draw onto the screen.

… will update this answer with a workaround when I’ve verified that this works …

Edit: Added workaround in a new post after testing.

2 Likes

I built a quick, dirty and hopefully not too complicated way to achieve what you were trying to do here:

Your main script with a few additions:

// osc send & receive tester 
import oscP5.*;
import netP5.*;

OscP5 oscP5;
NetAddress myRemoteLocation;

// store dots here when you receive them and draw them later
ArrayList<Dot> receivedDots = new ArrayList<Dot>();

void setup() {
  size(400,400);
  frameRate(25);
  background(40);
  noStroke();

  oscP5 = new OscP5(this,12000);
  
  myRemoteLocation = new NetAddress("127.0.0.1",12000);
}

void draw() {
  // This is the main thread, picking up new dots and drawing them
  for(Dot dot : receivedDots) {
    dot.draw();
  }
  // Empty the list of new dots so they are not drawn over and over again.
  // This is NOT a safe way of doing this. Some dots might be deleted
  //  before they are drawn.
  receivedDots.clear();
}

void bigDot (float x, float y) {
  //fill(255,100);
  //ellipseMode(CENTER);
  //ellipse(x*width,y*height,50,50);
  // This is running in the background. Can't draw here so
  //  just add a new dot to the list. It will be drawn later.
  receivedDots.add(new Dot(x, y, 50));
  println("creating bigDot   "+x+" "+y);
}

void smallDot (float x, float y) {
  //fill(255,100);
  //ellipseMode(CENTER);
  //ellipse(x*width,y*height,30,30);
  // This is running in the background. Can't draw here so
  //  just add a new dot to the list. It will be drawn later.
  receivedDots.add(new Dot(x, y, 30));
  println("creating smallDot "+x+" "+y);
}

void mousePressed() {
  println("---");
  OscMessage myMessage = new OscMessage("/test");
  float x = float(mouseX)/width;
  float y = float(mouseY)/height;
  myMessage.add(x); /* add a float to the osc message */
  myMessage.add(y); /* add a float to the osc message */
  bigDot(x,y);
  oscP5.send(myMessage, myRemoteLocation); 
}

void oscEvent(OscMessage theOscMessage) {
  if(theOscMessage.checkAddrPattern("/test")==true) {
    if(theOscMessage.checkTypetag("ff")) {
      float x = theOscMessage.get(0).floatValue();  
      float y = theOscMessage.get(1).floatValue();
      smallDot(x,y);
      return;
    }  
  }
  println("received unrecognized osc message");
}

A new class to temporarily store new dots:
(On your sketch open a new tab to paste this)

public class Dot{
  float x;
  float y;
  float diameter;
  
  public Dot(float _x, float _y, float _diameter) {
    x = _x;
    y = _y;
    diameter = _diameter;
  }
  
  public void draw() {
    fill(255,100);
    ellipseMode(CENTER);
    ellipse(x*width,y*height,diameter,diameter);
    println("drawing       Dot with diameter "+diameter+" at position "+x+" "+y);
  }
}

I added comments to new lines of code. Feel free to ask if the explanation isn’t clear or you have further questions. :slight_smile:

2 Likes

Thank you. I will try that, and I understand why it should work.

Can you tell me the difference between the mousePressed() event and the oscEvent() that allows mousePressed() to draw to the screen while oscEvent() cannot?

My own workaround was clumsy but also worked. The point of my exercise is to see how the sending and receiving of osc messages works between multiple sketches or other osc-capable processes, so I’ll proceed with that.

1 Like

That is a very good question to ask :smiley:
Mouse input is simply handled by the main thread (and that’s where you can draw in).

In general: Like with our workarounds there are ways to notice events in the background and make them do something in the main thread. If you’re willing to dive into it you could for example build a system that takes these network events in the background and creates new events just like them on the main thread.

As you were only using this to test sending and receiving from multiple sketches I didn’t bother with anything like that though. I figured you only need the ellipse to visualize if your messages are being received properly.

A small improvement for my code posted above is to swap out the draw function:

void draw() {
  // This is the main thread, picking up new dots and drawing them
  // Picking one each frame
  if(receivedDots.size() > 0) {
    // Grab one new dot
    Dot dot = receivedDots.get(0);
    // Remove it from the list so it is not drawn over and over again
    receivedDots.remove(dot);
    // Actually draw the dot you just took from the list
    dot.draw();
  }
}

This version should avoid removing dots before they are drawn. It still might happen if the implementation of ArrayList isn’t threadsafe though.

As I limited it to only draw one dot per frame if you reduce the frameRate of your sketch to something like 2 you can really see in what order the network messages arrived.

2 Likes

That’s very nice. I’ll study that more.

I returned to my own simple workaround using global variables for a message received status and its x and y, only because it makes the code smaller and simpler, and I am about to post my new question.

I appreciate your help! I’m learning my coding mainly through the Processing tutorials and related resources, so it’s all pretty new to me and I’m not too sophisticated outside that bubble.

1 Like

Even though Processing enqueues input events in a separate Thread, it dequeues them in the main “Animation” Thread.

So it’s safe to “draw” when using Processing’s own input callbacks.

Now, if a 3rd-party library deals w/ hardware, its callbacks got a very high chance to happen in a separate Thread.

Therefore, it’s highly unsafe to mutate the main canvas under a separate Thread.

In order to find out which Thread is executing a method, just place this statement there: println(Thread.currentThread());

3 Likes

besides that very interesting ( drawing from functions or threads… ) info,
from what i understand your question is
? why i can not see the small dots? even the println says it’s drawn ?
and your provided print and snap show

---
bigDot   0.3975 0.3125
smallDot 0.3975 0.3125
---

so anytime you get first the big dot, then the small dot
at the same x,y position
and you paint them with the same fill color
best try, for the small dot, to be positioned with a

int offset=80;

ellipse(x*width+offest,y*height,30,30);

and change the color too.

1 Like

You are right that these are less than optimal conditions but if you are trying to say that the smaller dot would not be visible then you are mistaken. Note the actual fill color is

The alpha value is 100 of 256 so when both circles are drawn you can see the difference.

I used an offset for testing aswell but for what cucurbit plans to do an offset is not good. You should be able to see where the user clicked on other clients and they only receive the small dot.

I highly recommend your idea to change the color though.
Maybe even just add different stroke colors.
e.g. for the big dot:

stroke(255, 0, 0); // red

and for the small dot:

stroke(0, 255, 0); // green

Yes, I could have made the dots more differentiated, but since they were effective enough I used them as they were (the color scheme inherited from another testing sketch I had already made).