0

I'm trying to make an audio plugin which can connect to a local Java server and send it data through a socket (TCP). As I heard many nice things about it, I'm using Boost's ASIO library to do the work.

I'm having quite a strange bug in my code : my AudioUnit C++ client (which I use from inside a DAW, I'm testing with Ableton Live and Logic Pro) can connect to my Java server alright, but when I do a write operation, it seems my write is correctly executed only once (as in, I can monitor any incoming message on my Java server, and only the first message is seen)

I'm using the following code :

-- Inside the header :

boost::asio::io_service io_service;
boost::asio::ip::tcp::socket mySocket(io_service);
boost::asio::ip::tcp::endpoint myEndpoint(boost::asio::ip::address::from_string("127.0.0.1"), 9001);
boost::system::error_code ignored_error;

-- Inside my plugin's constructor

mySocket.connect(myEndpoint);

-- And when I try to send :

boost::asio::write(mySocket, boost::asio::buffer(datastring), ignored_error);

(you will notice that I do not close my socket, because I'd like it to live forever)

I don't think the problem comes from my Java server (though I could be wrong !), because I found out a way to make my C++ plugin "work correctly" and send all the messages I want : If I don't open my socket upon initializing my plugin, but directly when I try sending the message, every message is received by my remote server. Ie, every time I call sendMessage(), I do the following :

try {
    // Connect to the Java application
    mySocket.connect(myEndpoint);
    // Write the data
    boost::asio::write(mySocket, boost::asio::buffer(datastring), ignored_error);
    // Disconnect
    mySocket.close();
} catch (const std::exception & e) {std::cout << "Couldn't initialize socket\n";}

Still, I'm not too happy with this code : I have to send about 1000 messages per second - while that might not be humongous, but I don't think opening the socket and connecting to the end point everytime is efficient (it's a blocking operation too)

Any input which could lead me in the right direction would be greatly appreciated !

For more information, here's my code in a slightly more complete version (with the useless stuff trimmed to keep it short)

#include <cstdlib>
#include <fstream>
#include "PluginProcessor.h"
#include "PluginEditor.h"
#include "SignalMessages.pb.h"
using boost::asio::local::stream_protocol;

//==============================================================================
// Default parameter values
const int defaultAveragingBufferSize = 256;
const int defaultMode = 0;
const float defaultInputSensitivity = 1.0;
const int defaultChannel = 1;
const int defaultMonoStereo = 1;        //Mono processing


//==============================================================================

// Variables used by the audio algorithm
int nbBufValProcessed = 0;
float signalSum = 0;
// Used for beat detection
float signalAverageEnergy = 0;
float signalInstantEnergy = 0;
const int thresholdFactor = 5;
const int averageEnergyBufferSize = 11025;                      //0.25 seconds

//==============================================================================

// Socket used to forward data to the Processing application, and the variables associated with it
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket mySocket(io_service);
boost::asio::ip::tcp::endpoint myEndpoint(boost::asio::ip::address::from_string("127.0.0.1"), 9001);
boost::system::error_code ignored_error;

//==============================================================================

SignalProcessorAudioProcessor::SignalProcessorAudioProcessor()
{
    averagingBufferSize             = defaultAveragingBufferSize;
    inputSensitivity                = defaultInputSensitivity;
    mode                            = defaultMode;
    monoStereo                      = defaultMonoStereo;
    channel                         = defaultChannel;


    // Connect to the remote server
    // Note for stack overflow : this is where I'd like connect to my server !
    mySocket.connect(myEndpoint);

}

SignalProcessorAudioProcessor::~SignalProcessorAudioProcessor()
{
}


//==============================================================================


void SignalProcessorAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{

    // In case we have more outputs than inputs, clear any output
    // channels that doesn't contain input data
    for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    //////////////////////////////////////////////////////////////////
    // This is the most important part of my code, audio processing takes place here !
    // Note for stack overflow : this shouldn't be very interesting, as it is not related to my current problem

    for (int channel = 0; channel < std::getNumInputChannels(); ++channel)
    {
        const float* channelData = buffer.getReadPointer (channel);
        for (int i=0; i<buffer.getNumSamples(); i++) {
            signalSum += std::abs(channelData[i]);
            signalAverageEnergy = ((signalAverageEnergy * (averageEnergyBufferSize-1)) + std::abs(channelData[i])) / averageEnergyBufferSize;
        }

    }

    nbBufValProcessed += buffer.getNumSamples();
    if (nbBufValProcessed >= averagingBufferSize) {

        signalInstantEnergy = signalSum / (averagingBufferSize * monoStereo);

        // If the instant signal energy is thresholdFactor times greater than the average energy, consider that a beat is detected
        if (signalInstantEnergy > signalAverageEnergy*thresholdFactor) {
            //Set the new signal Average Energy to the value of the instant energy, to avoid having bursts of false beat detections
            signalAverageEnergy = signalInstantEnergy;

            //Create an impulse signal - note for stack overflow : these are Google Protocol buffer messages, serialization is faster this way
            Impulse impulse;
            impulse.set_signalid(channel);
            std::string datastringImpulse;
            impulse.SerializeToString(&datastringImpulse);
            sendMessage(datastringImpulse);
        }

        nbBufValProcessed = 0;
        signalSum = 0;
    }
}

//==============================================================================

void SignalProcessorAudioProcessor::sendMessage(std::string datastring) {

    try {        
        // Write the data
        boost::asio::write(mySocket, boost::asio::buffer(datastring), ignored_error);

    } catch (const std::exception & e) {
        std::cout << "Caught an error while trying to initialize the socket - the Java server might not be ready\n";
        std::cerr << e.what();
    }
}


//==============================================================================

// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new SignalProcessorAudioProcessor();
}
Martin
  • 1
  • 1
  • 3
  • There's nothing obviously wrong with the code that you're showing. But then, you're not showing much of the code that **doesn't** work. Provide an [SSCCE](http://sscce.org/). – dhavenith Sep 19 '14 at 09:20
  • I added some code to be more precise – Martin Sep 19 '14 at 10:00
  • If the problem disappears when you open & close the socket for each message, you likely have a message framing problem. How is your server reading these messages? What is the message format? – Sam Miller Sep 19 '14 at 21:46
  • I'm using Google Protocol buffers to format my messages, so it's raw binary - among my messages, the simplest I want to send is a single int. To read the messages, I'm using code which looks quite a bit like the Google protobuf examples (in Java). A thread does the following continuously serviceSocket = audioDataServer.accept(); DataInputStream input = new DataInputStream(serviceSocket.getInputStream()); int lengthAvailable = input.available(); byte[] buf = new byte[lengthAvailable]; //Read the full data into the buffer input.readFully(buf); – Martin Sep 20 '14 at 22:13

0 Answers0