changeset 111:9928b6366227 scope-refactoring

Refactored the Scope class into NetworkSend and Scope classes. No need for a global pointer anymore!
author Giulio Moro <giuliomoro@yahoo.it>
date Wed, 19 Aug 2015 22:40:05 +0100
parents fb56681ab1d6
children 3168919fdb07
files core/NetworkSend.cpp include/NetworkSend.h include/Scope.h projects/scope/render.cpp
diffstat 4 files changed, 231 insertions(+), 69 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/NetworkSend.cpp	Wed Aug 19 22:40:05 2015 +0100
@@ -0,0 +1,144 @@
+//scope.cpp
+#include <Scope.h>
+
+#define BUILD_FOR_UDPRECEIVE_PLUGIN
+#define NETWORK_AUDIO_BUFFER_SIZE 302
+
+//initialize the static members of NetworkSend
+bool NetworkSend::staticConstructed=false;
+std::vector<NetworkSend*> NetworkSend::objAddrs(0);
+AuxiliaryTask NetworkSend::transmitAudioTask=NULL;
+
+void transmitAudio(){
+	NetworkSend::sendAllData();
+}
+
+void NetworkSend::sendAllData(){
+	for(unsigned int n=0; n<NetworkSend::objAddrs.size(); n++){
+		NetworkSend::objAddrs[n]->sendData();
+	}
+}
+
+void NetworkSend::staticConstructor(){
+	if(staticConstructed==true)
+		return;
+	staticConstructed=true;
+	transmitAudioTask = BeagleRT_createAuxiliaryTask(transmitAudio, 95, "transmitAudioTask"); //TODO: allow variable priority
+};
+
+NetworkSend::NetworkSend()
+{
+	sampleCount = 0;
+	channel.doneOnTime=true;
+	channel.index=channel.headerLength; //leave space for the heading message (channel, timestamp)
+	channel.timestamp=0;
+	channel.activeBuffer=0;
+	channel.readyToBeSent=false;
+}
+NetworkSend::~NetworkSend(){
+	for(unsigned int n=0; n<objAddrs.size(); n++){ //keep track of deleted instances;
+		if(objAddrs[n]==this){
+			objAddrs.erase(objAddrs.begin()+n);
+			break;
+		}
+	}
+}
+
+void NetworkSend::setup(float aSampleRate){
+	setup(aSampleRate, 0, 9999, "192.168.7.1");//channelNumber=0
+}
+
+void NetworkSend::setup(float aSampleRate, int aChannelNumber, int aPort, const char *aServer){
+	staticConstructor(); //FIXME: ideally this should be in the constructor, but this is not currently possible
+						//because of limitations in BeagleRT_createAuxiliaryTask()
+	//keep track of added active instances
+	objAddrs.push_back(this);//TODO: this line should be in the constructor, but something weird happens if
+	// an instance of NetworkSend is then declared globally: the constructor gets called,
+	// and objAddrs.size()==1 but when you get to setup, objAddrs.size() has reverted back to 0, without
+	// any destructor being called in between ...
+	setChannelNumber(aChannelNumber);
+	setPort(aPort);
+	setServer(aServer);
+	printf("Channel %d is sending messages to : %s:%d at %fHz\n", getChannelNumber(), aServer, aPort, aSampleRate);
+}
+
+
+void NetworkSend::log(float value){ //TODO: add a vectorized version of this method
+	if(channel.index==(NETWORK_AUDIO_BUFFER_SIZE)){ // when the buffer is ready ...
+		channel.readyToBeSent=true;
+		channel.index=channel.headerLength; //reset the counter
+		if(channel.doneOnTime==false){
+			printf("Network buffer underrun. timestamp: %d :-{\n", (int)channel.buffers[!channel.activeBuffer][1]);
+		}
+		channel.timestamp=sampleCount;
+		channel.activeBuffer=!channel.activeBuffer; //switch buffer
+		channel.doneOnTime=false;
+		BeagleRT_scheduleAuxiliaryTask(NetworkSend::transmitAudioTask); //send the buffer
+		//TODO: maybe we should have transmitAudioTask running in a loop instead of scheduling it multiple times?
+	}
+	if(channel.index==channel.headerLength){
+		channel.buffers[channel.activeBuffer][0] = (float)channel.channelNumber; //TODO: this could actually be done just once in setup()
+		channel.buffers[channel.activeBuffer][1]=(float)sampleCount; //timestamp
+		//add here more header fields
+	}
+    channel.buffers[channel.activeBuffer][channel.index++]=value;
+	sampleCount++;
+};
+
+void NetworkSend::setServer(const char *aServer){
+	udpClient.setServer(aServer);
+}
+void NetworkSend::setPort(int aPort){
+	udpClient.setPort(aPort);
+}
+
+void NetworkSend::setChannelNumber(int aChannelNumber){
+	channel.channelNumber=aChannelNumber;
+};
+int NetworkSend::getChannelNumber(){
+	return channel.channelNumber;
+};
+
+void NetworkSend::sendData(){
+	if(channel.readyToBeSent){
+		channel.readyToBeSent=false;
+		udpClient.send(
+			channel.buffers[!channel.activeBuffer],
+			NETWORK_AUDIO_BUFFER_SIZE*sizeof(float)
+		);
+		channel.doneOnTime=true;
+	}
+}
+
+int NetworkSend::getNumInstances(){
+	return objAddrs.size();
+};
+
+Scope::Scope(int aNumChannels):
+		channels(aNumChannels)
+{};
+Scope::~Scope(){};
+
+void Scope::log(int channel, float value){
+	if(channel>=getNumChannels()) //TODO: assert this
+		return;
+	channels[channel].log(value);
+}
+
+void Scope::setup(){
+	setup(44100, 9999, "127.0.0.1");
+}
+
+void Scope::setup(float sampleRate, int aPort, const char* aServer){
+	for(int n=0; n<getNumChannels(); n++){
+		channels[n].setup(sampleRate, n, aPort, aServer);
+	}
+}
+
+int Scope::getNumChannels(){
+	return channels.size();
+}
+
+void Scope::sendData(){
+	NetworkSend::sendAllData();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/NetworkSend.h	Wed Aug 19 22:40:05 2015 +0100
@@ -0,0 +1,67 @@
+//scope.cpp
+#ifndef SCOPE_H_
+#define SCOPE_H_
+
+#include <BeagleRT.h> 
+#include <rtdk.h>
+#include <cmath>
+#include <UdpClient.h>
+#include <vector>
+
+#define NETWORK_AUDIO_BUFFER_SIZE 302
+
+struct NetworkBuffer{
+	int timestamp;
+	int channelNumber;
+	int activeBuffer;
+	int index;
+	float buffers[2][NETWORK_AUDIO_BUFFER_SIZE]; 
+	bool doneOnTime;
+	bool readyToBeSent;
+	static const int headerLength=2;
+};
+
+class NetworkSend {
+    int sampleCount; 
+    float sampleRate;
+	UdpClient udpClient;
+	static bool staticConstructed;
+    static void staticConstructor();
+    static AuxiliaryTask transmitAudioTask; //TODO: allow different AuxiliaryTasks for different priorities (e.g.: audio vs scope)
+    static std::vector<NetworkSend *> objAddrs;
+  public:
+    NetworkBuffer channel;
+    NetworkSend();
+    ~NetworkSend();
+    void setup(float aSampleRate);
+    void setup(float aSampleRate, int aChannelNumber, int aPort, const char *aServer);
+    void sendData();
+    void log(float value);
+    void setPort(int aPort);
+    void setServer(const char* aServer);
+    void setChannelNumber(int aChannelNumber);
+    int getChannelNumber();
+    static int getNumInstances();
+    static void sendAllData();
+};
+
+/**
+ * An array of NetworkSend objects with some default parameters
+ *
+ * All sending on the same port (defaults to 9999)
+ * All sending to the same server (defaults to 127.0.0.1)
+*/
+class Scope {
+	std::vector<NetworkSend> channels;
+	void deallocate();
+public:
+	Scope(int aNumChannels);
+	~Scope();
+	void log(int channel, float value);
+    void setup();
+    void setup(float sampleRate, int aPort, const char* aServer);
+    void sendData();
+    void setPort();
+    int getNumChannels();
+};
+#endif /* SCOPE_H */
--- a/include/Scope.h	Wed Aug 19 22:36:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-//scope.cpp
-#ifndef SCOPE_H_
-#define SCOPE_H_
-
-#include <BeagleRT.h> 
-#include <rtdk.h>
-#include <cmath>
-#include <UdpClient.h>
-
-#define BUILD_FOR_UDPRECEIVE_PLUGIN
-#define NETWORK_AUDIO_BUFFER_SIZE 302
-
-struct NetworkBuffer{
-	int timestamp;
-	int channelNumber;
-	int activeBuffer;
-	int index;
-	float buffers[2][NETWORK_AUDIO_BUFFER_SIZE]; 
-	bool doneOnTime;
-	bool readyToBeSent;
-	int headerLength=2;
-	UdpClient udpClient;
-};
-
-#define NUM_SCOPE_CHANNELS 6
-
-class NetworkIO {
-    int sampleCount; 
-    float sampleRate;
-    AuxiliaryTask scopeTask;
-    int port;
-  public:
-    NetworkBuffer channel;
-    NetworkIO();
-    ~NetworkIO();
-    void setup(float aSampleRate);
-    void setup(float aSampleRate, int aChannelNumber);
-    void sendData();
-    void log(float value);
-    void setPort(int aPort);
-    int getPort();
-    void setChannelNumber(int aChannelNumber);
-    int getChannelNumber();
-};
-
-class Scope {
-	NetworkIO *channels;
-	int numChannels;
-	void deallocate();
-public:
-	Scope(int aNumChannels);
-	~Scope();
-	void log(int channel, float value);
-    void setup(float sampleRate);
-    void sendData();
-};
-#endif /* SCOPE_H */
--- a/projects/scope/render.cpp	Wed Aug 19 22:36:45 2015 +0100
+++ b/projects/scope/render.cpp	Wed Aug 19 22:40:05 2015 +0100
@@ -6,7 +6,8 @@
 float gFrequency1, gFrequency2;
 float gInverseSampleRate;
 
-Scope scope(1);    //create a scope object with numChannels
+Scope scope(6);   //create a scope object with 6 channels
+NetworkSend networkSend;
 
 // initialise_render() is called once before the audio rendering starts.
 // Use it to perform any initialisation and allocation which is dependent
@@ -16,9 +17,11 @@
 // in from the call to initAudio().
 //
 // Return true on success; returning false halts the program.
+
 bool setup(BeagleRTContext *context, void *userData)
 {
-	scope.setup(context->audioSampleRate);  //call this once in setup to initialise the scope
+	scope.setup();  //call this once in setup to initialise the scope
+//	networkSend.setup(context->audioSampleRate, 0, 9999, "192.168.7.1");
 	 
 	gInverseSampleRate = 1.0/context->audioSampleRate;
 	
@@ -27,6 +30,7 @@
 	
 	gFrequency1 = 200.0;
 	gFrequency2 = 201.0;
+
 	return true; 
 }
 
@@ -40,16 +44,20 @@
 	static int count=0;
 	for(unsigned int n = 0; n < context->audioFrames; n++) {
 	    
-		float chn1 = sinf(gPhase1);
-	    float chn2 = sinf(gPhase2);
-	    
-	    float chn3 = context->audioIn[n*2 + 0];
-	    float chn4 = context->audioIn[n*2 + 1];
-	    
-	    float chn5 = context->analogIn[(int)n/2*8 + 0];
-	    float chn6 = context->analogIn[(int)n/2*8 + 1];
-		if((n&1)==0)
-		    scope.log(0, chn1);
+		float chn0 = sinf(gPhase1);
+	    float chn1 = sinf(gPhase2);
+
+	    float chn2 = context->audioIn[n*2 + 0];
+	    float chn3 = context->audioIn[n*2 + 1];
+
+	    float chn4 = context->analogIn[(int)n/2*8 + 0];
+	    float chn5 = context->analogIn[(int)n/2*8 + 1];
+		scope.log(0, chn0);
+		scope.log(1, chn1);
+		scope.log(2, chn2);
+		scope.log(3, chn3);
+		scope.log(4, chn4);
+		scope.log(5, chn5);
 	    
 //	    scope.log(chn1, chn2, chn3, chn4, chn5, chn6);
 	    //call this once every audio frame