as you are still using int, which is a 4-byte integer, that would mean that the top two bytes of each element are set to 0, i.e.: your values are all below 65536. If you were to use uint16_t instead of int, you'd get 2 bytes per element, so half the bandwidth and no zeros.

    5 days later

    giuliomoro Just was able to try this - thank you!

    The uint16_t does reduce the length of the message which is very helpful. That said, it seems that samples # 8-15 for each of the analog input's blocks are zeros. I've included 1 message from the Pd console below as well as the code. Any ideas?

    Thanks again!

    186 107 191 107 163 107 171 107 167 107 151 107 165 107 176 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 106 108 92 108 84 108 94 108 74 108 94 108 102 108 93 108 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 172 107 149 107 173 107 178 107 197 107 195 107 190 107 193 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 40 108 20 108 240 107 245 107 0 108 233 107 224 107 246 107 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 120 0 113 0 128 0 125 0 130 0 117 0 121 0 120 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 152 0 172 0 162 0 170 0 150 0 181 0 161 0 162 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 162 0 145 0 143 0 150 0 158 0 147 0 149 0 142 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 143 0 166 0 148 0 166 0 140 0 146 0 157 0 158 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    #include <Bela.h>
    #include <libraries/Pipe/Pipe.h>
    #include <libraries/Serial/Serial.h>
    #include <cmath>
    #include <iostream>
    #include <string>
    #include <libraries/UdpClient/UdpClient.h>
    
    
    float input; //from Bela->context
    uint16_t array[8][16]; //eight ADC x 16 samples
    UdpClient udpClient(4567, "192.168.7.1");
    Serial gSerial;
    Pipe gPipe;
    AuxiliaryTask serialCommsTask;
    
    
    
    void ioLoop(void* arg) {
    	while(!Bela_stopRequested())
    	{
    		udpClient.send(array, sizeof(array));
    	//	usleep(100);
    	}
    }
    
    bool setup(BelaContext *context, void *userData) {
    	gSerial.setup ("/dev/ttyGS0", 230400);
    	AuxiliaryTask serialCommsTask = Bela_createAuxiliaryTask(ioLoop, 0, "serial-thread", NULL);
    	Bela_scheduleAuxiliaryTask(serialCommsTask);
    	gPipe.setup("serialpipe", 1024);
    	return true;
    }
    
    void render(BelaContext *context, void *userData)
    {
    //READ FRAME BY FRAME OF THE BUFFER
    	for(unsigned int n = 0; n < context->audioFrames; n++) {
    	for(unsigned int ar = 0; ar < 8; ar++) {
    	input = analogRead(context, n, ar); //read from ADC/context
    	array[ar][n] = trunc(input*65535); //convert from float to int
        }
        }
    }
    
    void cleanup(BelaContext *context, void *userData) {}

      violapwr for(unsigned int n = 0; n < context->audioFrames;

      You are going through n audioFrames but then reading analog inputs. As you have 8 analog inputs, analogSamplingRate is half of audioSamplingRate and analogFrames are half of of the audioFrames. This means that - with the default blocksize of 16 audio frames per block - you only have 8 analog frames.

      Change the size of your array to [8][8] and use context->analogFrames instead of context->audioFrames.

        giuliomoro ahh yes that definitely makes sense. this is working well now - thank you!

        There's one last thing I'm seeking to do/fix: create a consistent transmission rate from the Bela to PD on the computer. I'm measuring a range of 15000-18000 blocks/second in Pd -- I imagine the theoretical rate should be 2756.25 blocks/second since we have 8 samples/block and 22050 samples/second. I'm guessing here, but it seems that even though render() is called at period intervals perhaps the void ioLoop is making additional cycles? Even when I add usleepthe transmission rate will slow down, but is still not consistent.

        Any ideas? thank you again! 🙂

        #include <Bela.h>
        #include <libraries/Pipe/Pipe.h>
        #include <libraries/Serial/Serial.h>
        #include <cmath>
        #include <iostream>
        #include <string>
        #include <libraries/UdpClient/UdpClient.h>
        
        
        float input; //from Bela->context
        uint16_t array[8][8]; //eight ADC x 8 samples
        UdpClient udpClient(4567, "192.168.7.1");
        Serial gSerial;
        Pipe gPipe;
        AuxiliaryTask serialCommsTask;
        
        
        
        void ioLoop(void* arg) {
        	while(!Bela_stopRequested())
        	{
        		udpClient.send(array, sizeof(array));
        		usleep (100);
        	}
        }
        
        bool setup(BelaContext *context, void *userData) {
        	gSerial.setup ("/dev/ttyGS0", 230400);
        	AuxiliaryTask serialCommsTask = Bela_createAuxiliaryTask(ioLoop, 0, "serial-thread", NULL);
        	Bela_scheduleAuxiliaryTask(serialCommsTask);
        	gPipe.setup("serialpipe", 1024);
        	return true;
        }
        
        void render(BelaContext *context, void *userData)
        {
        //READ FRAME BY FRAME OF THE BUFFER
        	for(unsigned int n = 0; n < context->analogFrames; n++) {
        	for(unsigned int ar = 0; ar < 8; ar++) {
        	input = analogRead(context, n, ar); //read from ADC/context
        	array[ar][n] = trunc(input*65535); //convert from float to int
            }
            }
        }
        
        void cleanup(BelaContext *context, void *userData) {}

          violapwr I'm measuring a range of 15000-18000 blocks/second in PD -- I imagine the theoretical rate should be 2756.25 blocks/second since we have 8 samples/block and 22050 samples/second.

          The first question to ask is: in the Bela code, what is limiting the rate at which udpClient.send() is called? If this was the only thread running on the board, it would be limited by the sum of the usleep() time and the time it takes to actually execute send(). As you are running the audio thread and other processes on the board, that value becomes the maximum rate, but the actual rate will be lower depending on the audio thread's and other processes' CPU usage. also have no guarantee of the interval between executions of the udpClient.send(). A call to usleep() guarantees that the thread will sleep at least the specified number of seconds, but makes no guarantees about the maximum sleep amount. Also, send() could be preempted by other higher-priority threads once or more during its execution.

          So the answer is: it depends, but there is no reason why it should be 2756.25 blocks/second : as you see, your code ioLoop() makes no attempt to synchronise with the execution of the audio thread. The loop may run 0, 1 or several times in between executions of render() and you have no guarantees of when or how frequently on average that will happen. You could use a circular buffer being written by render() read by ioLoop(). ioLoop() would sleep and periodically wake up to check if any data is available to read and if it is it would read it and send it over the network.

            giuliomoro Yes that totally makes sense - thank you!

            I think this most likely solves this feature update for now - thank you again for your thoughtful responses and help through this. Very excited to see how this fits into my overall design.

            Here's what I ended up with in case helpful for others down the road:

            #include <Bela.h>
            #include <libraries/Pipe/Pipe.h>
            #include <libraries/Serial/Serial.h>
            #include <cmath>
            #include <iostream>
            #include <string>
            #include <libraries/UdpClient/UdpClient.h>
            
            
            float input; //from Bela->context
            bool NewBlock;
            uint16_t array[8][8]; //eight ADC x 8 samples
            UdpClient udpClient(4567, "192.168.7.1");
            Serial gSerial;
            Pipe gPipe;
            AuxiliaryTask serialCommsTask;
            
            
            
            void ioLoop(void* arg) {
            	while(!Bela_stopRequested())
            	{
            		if (NewBlock == true) { 
            		udpClient.send(array, sizeof(array));
            		NewBlock = false;
            		}
            		
            		usleep (80);
            	}
            }
            
            bool setup(BelaContext *context, void *userData) {
            	gSerial.setup ("/dev/ttyGS0", 230400);
            	AuxiliaryTask serialCommsTask = Bela_createAuxiliaryTask(ioLoop, 0, "serial-thread", NULL);
            	Bela_scheduleAuxiliaryTask(serialCommsTask);
            	gPipe.setup("serialpipe", 1024);
            	return true;
            }
            
            void render(BelaContext *context, void *userData)
            {
            //READ FRAME BY FRAME OF THE BUFFER
            	for(unsigned int n = 0; n < context->analogFrames; n++) {
            	for(unsigned int ar = 0; ar < 8; ar++) {
            	input = analogRead(context, n, ar); //read from ADC/context
            	array[ar][n] = trunc(input*65535); //convert from float to int
                }
                }
            	NewBlock = true;
            }
            
            void cleanup(BelaContext *context, void *userData) {}

            I have to point out that your code is not thread safe and you could have some cases where the ioLoop thread doesn't run in between executions of render() (e.g.: because the system is busy doing something else and doesn't manage to schedule it on time), thus losing one block worth of readings. Having a circular buffer of adequate size would guarantee that no blocks are lost. Also, in principle there is no guarantee that the array variable's content will be fully written by the time the ioLoop thread sees that NewBlock is true, though for a single-core system like this, where ioLoop is guaranteed not to run alongside and/or preempt render(), then you'll probably be fine in practice.

            Thanks for sharing your code. To make it more useful to the you probably want to remove stale references to serial and unused variables/unneeded includes.

            What's all this for, btw?

              giuliomoro ahh got it - yes those are great suggestions - thank you!

              how's this update look? I've removed the stale references and implemented a circular buffer. any/all suggestions welcome-- thank you again!

              #include <Bela.h>
              #include <cmath>
              #include <iostream>
              #include <libraries/UdpClient/UdpClient.h>
              
              constexpr int kNumChannels = 8; // Number of ADC channels
              constexpr int kBufferSize = 64; // Total size of the circular buffer
              constexpr int kSamplesPerChannel = 8; //Number of samples transmitted per cycle per channel
              
              float input; //from Bela->context
              uint16_t circularBuffer[kNumChannels][kBufferSize]; // Circular buffer
              UdpClient udpClient(4567, "192.168.7.1");
              AuxiliaryTask serialCommsTask;
              
              int readIndex = 0;  // Current read index
              int writeIndex = 0; // Current write index
              
              void ioLoop(void* arg) {
                  while (!Bela_stopRequested()) {
                      if (readIndex != writeIndex) {
                          uint16_t buffer[kNumChannels][kSamplesPerChannel];
                          
                          for (int channel = 0; channel < kNumChannels; ++channel) {
                              for (int sample = 0; sample < kSamplesPerChannel; ++sample) {
                                  int index = (readIndex + sample) % kBufferSize;
                                  buffer[channel][sample] = circularBuffer[channel][index];
                              }
                          }
                  
                          udpClient.send(buffer, sizeof(buffer));
                          readIndex = (readIndex + kSamplesPerChannel) % kBufferSize; // Move the read index to the next block
                      }
                      
                      usleep(30);
                  }
              }
              
              
              bool setup(BelaContext *context, void *userData) {
              	AuxiliaryTask serialCommsTask = Bela_createAuxiliaryTask(ioLoop, 0, "serial-thread", NULL);
              	Bela_scheduleAuxiliaryTask(serialCommsTask);
              	return true;
              }
              
              void render(BelaContext *context, void *userData)
              {
              	for(unsigned int n = 0; n < context->analogFrames; n++) {
              	for(unsigned int ar = 0; ar < kNumChannels; ar++) {
              	input = analogRead(context, n, ar); //read from ADC/context
              	circularBuffer[ar][writeIndex] = trunc(input*65535); //convert from float to int
                  }
                  writeIndex = (writeIndex + 1) % kBufferSize; // Increment write index in a circular manner
                  }
              }
              
              void cleanup(BelaContext *context, void *userData) {}

              the code is for this instrument: www.instagram.com/p/CfhfSTBl-nB/

                violapwr the code is for this instrument: www.instagram.com/p/CfhfSTBl-nB/

                That's nice, I remember it struck me when it was announced last year: it seemed like it was doing all the right things.

                Now a question I've had for a while about what you are trying to do with Bela: why are you sending data over network from Bela to the host computer?

                  giuliomoro ahh that's awesome to hear -- the instrument is coming along !! 🙂

                  a few years back (when I first got the Bela) I ported the Pd code for the instrument from the Mac to the Bela. The problem was that Heavy doesn't support some objects; FFT was especially an issue (you had helped me to understand the scenario here: https://forum.bela.io/d/1698-optimizing-pd-patch-heavy-compatibility-other-ideas). Because I was caught up in hardware design stuff at that time, I decided best route was to keep the software running on the Mac, stick with the Arduino I was using for ADC, and later, down the road, work to re-code everything in C++ for the Bela. I'm at the point where a lot of the HW stuff is fixed and am now switching back to the Bela for the ADC (better conversion in comparison to Arduino). I'm still kicking the can down the road for the C++ port lol. Anyway, that's the backstory 🙂

                    9 months later

                    violapwr wanted to share an update on this in case useful to anyone; I finally got all the bugs out in this Bela analog data -> PD function I was building.
                    -data is being transmitted with minimal errors and audio signals (over analog) actually sound good (as opposed to crunchy lol) when received on the PD end
                    -uses OSC to send & sync sample rate, block size, and number of channels to PD

                    below is the Bela code and here's link to the PD files:
                    https://github.com/brianlindgren/Bela-to-PD-over-OSC

                    #include <Bela.h>
                    #include <cmath>
                    #include <iostream>
                    #include <libraries/UdpClient/UdpClient.h>
                    #include <libraries/OscSender/OscSender.h>
                    #include <libraries/OscReceiver/OscReceiver.h>
                    #include <libraries/Pipe/Pipe.h>
                    
                    Pipe oscPipe;
                    
                    OscReceiver oscReceiver;
                    OscSender oscSender;
                    int localPort = 7562;
                    int remotePort = 5000;
                    const char* remoteIp = "192.168.7.1";
                    
                    constexpr int kBufferSize = 1024; // Total size of the circular buffer
                    int kAnalogInChannels;
                    int gAnalogFrames;
                    int gAnalogSampleRate;
                    
                    float input; //from Bela->context
                    uint16_t circularBuffer[kBufferSize]; // Circular buffer
                    UdpClient udpClient(4567, "192.168.7.1");
                    AuxiliaryTask serialCommsTask;
                    
                    int readIndex = 0;  // Current read index
                    int writeIndex = 0; // Current write index
                    
                    
                    void ioLoop(void* arg) {
                        while (!Bela_stopRequested()) {
                            while (readIndex != writeIndex) { //send 1 block at time until caught up
                            
                                uint16_t buffer[kAnalogInChannels * gAnalogFrames]; // init transfer buffer
                                
                                //copy analog frames to transfer buffer
                                for (int frame = 0; frame < gAnalogFrames; ++frame) {
                                	//cycle through channels and copy content from circular buffer
                                    for (int channel = 0; channel < kAnalogInChannels; ++channel) {
                                        buffer[(frame * kAnalogInChannels) + channel] = circularBuffer[readIndex + ((frame * kAnalogInChannels) + channel)];
                                    }
                                }
                        
                                readIndex = (readIndex + (gAnalogFrames * kAnalogInChannels)); // Move the read index to the next block
                                
                                if (readIndex >= (kBufferSize - 1))  //wrap read pointer
                        			readIndex = 0;
                                
                    			udpClient.send(buffer, sizeof(buffer)); //send to PD
                            }
                            usleep(250);
                        }
                    }
                    
                    void on_receive(oscpkt::Message* msg, void*)
                    {
                    	// we make a copy of the incoming message and we send it down the pipe to the real-time thread
                    	oscpkt::Message* incomingMsg = new oscpkt::Message(msg);
                    	oscPipe.writeNonRt(incomingMsg);
                    
                    	// the real-time thread sends back to us the pointer once it is done with it
                    	oscpkt::Message* returnedMsg;
                    	while(oscPipe.readNonRt(returnedMsg) > 0)
                    	{
                    		delete returnedMsg;
                    	}
                    }
                    
                    bool setup(BelaContext *context, void *userData) {
                    	/// from Bela example
                    	oscPipe.setup("incomingOsc");
                    	oscReceiver.setup(localPort, on_receive);
                    	oscSender.setup(remotePort, remoteIp);
                    
                    	// the following code sends an OSC message to address /osc-setup
                    	oscSender.newMessage("/osc-setup").send();
                    
                    	printf("Waiting for handshake ....\n");
                    	// we want to stop our program and wait for a new message to come in.
                    	// therefore, we set the pipe to blocking mode.
                    	oscPipe.setBlockingNonRt(false);
                    	oscPipe.setBlockingRt(true);
                    	oscPipe.setTimeoutMsRt(1000);
                    	oscpkt::Message* msg = nullptr;
                    	int ret = oscPipe.readRt(msg);
                    	bool ok = false;
                    	if(ret > 0) {
                    		if(msg && msg->match("/osc-setup-reply"))
                    		{
                    			printf("handshake received!\n");
                    			ok = true;
                    		}
                    		delete msg;
                    	}
                    	if(!ok) {
                    		fprintf(stderr, "No handshake received: %d\n", ret);
                    		return false;
                    	}
                    	// in the remainder of the program, we will be calling readRt() from render(), and we want it
                    	// to return immediately if there are no new messages available. We therefore set the
                    	// pipe to non-blocking mode
                    	oscPipe.setBlockingRt(false);
                    	
                    	AuxiliaryTask serialCommsTask = Bela_createAuxiliaryTask(ioLoop, 90, "serial-thread", NULL);
                    	Bela_scheduleAuxiliaryTask(serialCommsTask);
                    	printf("AnalogFrames: %d\n", context->analogFrames);
                    	printf("AnalogInChannels: %d\n", context->analogInChannels);
                    	printf("AnalogSampleRate: %d\n", (int)context->analogSampleRate);
                    	gAnalogFrames = context->analogFrames; //# of analog frames
                    	kAnalogInChannels = context->analogInChannels;
                    	gAnalogSampleRate = (int)context->analogSampleRate;
                    	oscSender.newMessage("/osc-settings").add(gAnalogSampleRate).add(gAnalogFrames).add(kAnalogInChannels).send();
                    	oscPipe.writeRt(msg);
                    	return true;
                    }
                    
                    void render(BelaContext *context, void *userData)
                    {
                    	//write a message if read head couldn't catch up to write head last cycle
                    	if (readIndex != writeIndex) {
                            rt_printf("udp catchup! >> readIndex: %d, writeIndex: %d\n", readIndex, writeIndex);
                    	}
                    
                    	
                    	//cycle through frames
                    	for(unsigned int n = 0; n < gAnalogFrames; n++) {
                    	
                    		//cycle through channels and copy content to circular buffer
                    		for(unsigned int ar = 0; ar < kAnalogInChannels; ar++) {
                    			
                    			input = analogRead(context, n, ar); //read from ADC/context
                    			circularBuffer[writeIndex + ar] = input * 65535; //convert from float to int
                    	    }
                    	    // Increment write index every time a new frame is written and reset to 0 if too high
                    	    writeIndex = (writeIndex + kAnalogInChannels); 
                    	    
                    	    if (writeIndex >= (kBufferSize - 1))
                    	    	writeIndex = 0;
                        }
                    }
                    
                    void cleanup(BelaContext *context, void *userData) {}
                    a year later

                    very strange problem I'm having...

                    for some reason the Bela all of a sudden had trouble communicating with PD over OSC. I wiped and reflashed the ssd but still not working (with the code above). I'm getting this error: no viable conversion from 'void (oscpkt::Message *, void *)' to 'std::function<void (oscpkt::Message *, const char *, void *)>' column: 31, line: 73

                    if i run \example Communication/OSC-pipe/render.cpp the code runs & i'm able to receive 'osc-setup' in PD, but the 'osc-setup-reply' is not received on the Bela, with PD reporting this error send: No route to host (65)

                    come to think of it, a few days ago, git (on Bela) was giving me a seg fault, so i tried to update/reinstall it using apt (which did not work)... hopefully I did not screw up anything .... 😳

                    any ideas?

                      Probably your code needs to be amended tobe compatible with the code on the board. There was an API change which is what makes the example work but your project doesn't

                      It should be as easy as replacing

                      void on_receive(oscpkt::Message* msg, void*)

                      with

                      void on_receive(oscpkt::Message* msg, const char*, void*)

                      @giuliomoro that worked, the program now compiles! thanks!

                      however, i'm still getting this error (both my program and the example project) when PD responds to Bela with osc-setup-reply: send: No route to host (65). when i ping 192.168.7.2 from my computer, the Bela responds, so the IP seems to be correct...

                      any other ideas?

                      Can you run ifconfig on the board (in the console at the bottom of the IDE) and show the results here? Also, run ping -c 1 192.168.7.1 in there and see what comes back

                        giuliomoro OK, this is what was returned:

                        root@bela ~/Bela# ifconfig
                        bash: line 6: ifconfig: command not found

                        and

                        root@bela ~/Bela# ping -c 1 192.168.7.1
                        PING 192.168.7.1 (192.168.7.1) 56(84) bytes of data.
                        64 bytes from 192.168.7.1: icmp_seq=1 ttl=64 time=1.09 ms
                        --- 192.168.7.1 ping statistics ---
                        1 packets transmitted, 1 received, 0% packet loss, time 0ms
                        rtt min/avg/max/mdev = 1.099/1.099/1.099/0.000 ms

                        giuliomoro

                        root@bela ~/Bela# ip a
                        1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
                            link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
                            inet 127.0.0.1/8 scope host lo
                               valid_lft forever preferred_lft forever
                            inet6 ::1/128 scope host 
                               valid_lft forever preferred_lft forever
                        2: eth0:  mtu 1500 qdisc mq state DOWN group default qlen 1000
                            link/ether 4c:3f:d3:19:7a:91 brd ff:ff:ff:ff:ff:ff
                        3: wlan0:  mtu 1500 qdisc noop state DOWN group default qlen 1000
                            link/ether 1c:bf:ce:65:1a:fd brd ff:ff:ff:ff:ff:ff
                        4: usb0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
                            link/ether be:1a:d3:19:7a:93 brd ff:ff:ff:ff:ff:ff
                            inet 192.168.6.2/24 brd 192.168.6.255 scope global usb0
                               valid_lft forever preferred_lft forever
                            inet6 fe80::bc1a:d3ff:fe19:7a93/64 scope link 
                               valid_lft forever preferred_lft forever
                        5: usb1:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
                            link/ether be:1a:d3:19:7a:96 brd ff:ff:ff:ff:ff:ff
                            inet 192.168.7.2/24 brd 192.168.7.255 scope global usb1
                               valid_lft forever preferred_lft forever
                            inet6 fe80::bc1a:d3ff:fe19:7a96/64 scope link 
                               valid_lft forever preferred_lft forever