view core/NetworkSend.cpp @ 120:cdd441a304a9 scope-refactoring

Added read to interleaved buffer
author Giulio Moro <giuliomoro@yahoo.it>
date Fri, 21 Aug 2015 15:52:37 +0100
parents c692827083e1
children cce58e6ec2a2
line wrap: on
line source
//scope.cpp
#include <NetworkSend.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::sendDataTask=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;
	sendDataTask = BeagleRT_createAuxiliaryTask(transmitAudio, 95, "sendDataTask"); //TODO: allow variable priority
};

NetworkSend::NetworkSend()
{
	sampleCount = 0;
	channel.doneOnTime=true;
	channel.index=channel.headerLength; //leave space for the heading message (channel, timestamp)
	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.activeBuffer=!channel.activeBuffer; //switch buffer
		channel.doneOnTime=false;
		BeagleRT_scheduleAuxiliaryTask(NetworkSend::sendDataTask); //send the buffer
		// TODO: maybe we should have transmitAudioTask running in a loop instead of scheduling it multiple times?
		// The current solution allows to minimize latency when a single channel is used, as there is no inherent
		// rt_task_sleep in the thread, as we are signaling it every time.
		// Although, there is a possible race condition: if the auxiliaryTask is scheduled by channel 0,
		// it might still be executing when channel 1 schedules it. But if the AuxTask has already skipped
		// over channel 1, then we are at risk that channel 1 never gets sent.
	}
	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);
	}
}

void Scope::setPort(int port){
	for(int n=0; n<getNumChannels(); n++){
		channels[n].setPort(port);
	}
}
void Scope::setPort(int channel, int aPort){
	channels[channel].setPort(aPort);
	printf("Channel %d is now sending to port %d\n", channel, aPort);
}

int Scope::getNumChannels(){
	return channels.size();
}

void Scope::sendData(){
	NetworkSend::sendAllData();
}