view core/Scope.cpp @ 485:506a319c08cf prerelease

Scope can now take a float* to an array instead of individual variables
author Giulio Moro <giuliomoro@yahoo.it>
date Tue, 21 Jun 2016 15:17:38 +0100
parents efc9a9f8e63d
children
line wrap: on
line source
/***** Scope.cpp *****/
#include <Scope.h>

Scope::Scope():connected(0), triggerPrimed(false), started(false){}

// static aux task functions
void Scope::triggerTask(void *ptr){
    Scope *instance = (Scope*)ptr;
    if (instance->started)
        instance->doTrigger();
}
void Scope::sendBufferTask(void *ptr){
    Scope *instance = (Scope*)ptr;
    instance->sendBuffer();
}

void Scope::setup(unsigned int _numChannels, float _sampleRate){
    
    numChannels = _numChannels;
    sampleRate = _sampleRate;
    
    // setup the OSC server && client
    // used for sending / receiving settings
    // oscClient has it's send task turned off, as we only need to send one handshake message
    oscServer.setup(OSC_RECEIVE_PORT);
    oscClient.setup(OSC_SEND_PORT, "127.0.0.1", false);

    // setup the udp socket
    // used for sending raw frame data
    socket.setServer("127.0.0.1");
	socket.setPort(SCOPE_UDP_PORT);

	// setup the auxiliary tasks
	scopeTriggerTask = Bela_createAuxiliaryTask(Scope::triggerTask, BELA_AUDIO_PRIORITY-2, "scopeTriggerTask", this, true);
	scopeSendBufferTask = Bela_createAuxiliaryTask(Scope::sendBufferTask, BELA_AUDIO_PRIORITY-1, "scopeSendBufferTask", this);

    // send an OSC message to address /scope-setup
    // then wait 1 second for a reply on /scope-setup-reply 
    bool handshakeReceived = false;
    oscClient.sendMessageNow(oscClient.newMessage.to("/scope-setup").add((int)numChannels).add(sampleRate).end());
    oscServer.receiveMessageNow(1000);
    while (oscServer.messageWaiting()){
        if (handshakeReceived){
            parseMessage(oscServer.popMessage());
        } else if (oscServer.popMessage().match("/scope-setup-reply")){
            handshakeReceived = true;
        }
    }
    
    if (handshakeReceived && connected)
        start();
    
}

void Scope::start(){

    // resize the buffers
    channelWidth = frameWidth * FRAMES_STORED;
    buffer.resize(numChannels*channelWidth);
    outBuffer.resize(numChannels*frameWidth);

    // reset the pointers
    writePointer = 0;
    readPointer = 0;
    triggerPointer = 0;

    // reset the trigger
    triggerPrimed = true;
    triggerCollecting = false;
    triggerWaiting = false;
    triggerCount = 0;
    downSampleCount = 1;
    autoTriggerCount = 0;
    customTriggered = false;

    started = true;
}

void Scope::stop(){
    started = false;
}

void Scope::log(float* values){
	//TODO: contains lots of duplicated code from log(float,...).
	//TODO: needs refactoring
    // check for any received OSC messages
    while (oscServer.messageWaiting()){
        parseMessage(oscServer.popMessage());
    }

    if (!started) return;

    if (downSampling > 1){
        if (downSampleCount < downSampling){
            downSampleCount++;
            return;
        }
        downSampleCount = 1;
    }

    int startingWritePointer = writePointer;

    // save the logged samples into the buffer
    for (int i=0; i<numChannels; i++) {
        buffer[i*channelWidth + writePointer] = values[i];
    }

    writePointer = (writePointer+1)%channelWidth;

    // if upSampling > 1, save repeated samples into the buffer
    for (int j=1; j<upSampling; j++){

        buffer[writePointer] = buffer[startingWritePointer];

        for (int i=1; i<numChannels; i++) {
            buffer[i*channelWidth + writePointer] = buffer[i*channelWidth + startingWritePointer];
        }

        writePointer = (writePointer+1)%channelWidth;

    }

}
void Scope::log(float chn1, ...){

    // check for any received OSC messages
    while (oscServer.messageWaiting()){
        parseMessage(oscServer.popMessage());
    }
    
    if (!started) return;
    
    if (downSampling > 1){
        if (downSampleCount < downSampling){
            downSampleCount++;
            return;
        }
        downSampleCount = 1;
    }
    
    va_list args;
    va_start (args, chn1);
    
    int startingWritePointer = writePointer;
  
    // save the logged samples into the buffer
    buffer[writePointer] = chn1;

    for (int i=1; i<numChannels; i++) {
        // iterate over the function arguments, store them in the relevant part of the buffer
        // channels are stored sequentially in the buffer i.e [[channel1], [channel2], etc...]
        buffer[i*channelWidth + writePointer] = (float)va_arg(args, double);
    }

    writePointer = (writePointer+1)%channelWidth;
    
    // if upSampling > 1, save repeated samples into the buffer
    for (int j=1; j<upSampling; j++){
        
        buffer[writePointer] = buffer[startingWritePointer];
    
        for (int i=1; i<numChannels; i++) {
            buffer[i*channelWidth + writePointer] = buffer[i*channelWidth + startingWritePointer];
        }
    
        writePointer = (writePointer+1)%channelWidth;
        
    }
    
    va_end (args);
    
}

bool Scope::trigger(){
    if (triggerMode == 2 && !customTriggered && triggerPrimed && started){
        customTriggerPointer = (writePointer-xOffset+channelWidth)%channelWidth;
        customTriggered = true;
        return true;
    }
    return false;
}

void Scope::scheduleSendBufferTask(){
    Bela_scheduleAuxiliaryTask(scopeSendBufferTask);
}

bool Scope::triggered(){
    if (triggerMode == 0 || triggerMode == 1){  // normal or auto trigger
        return (!triggerDir && buffer[channelWidth*triggerChannel+((readPointer-1+channelWidth)%channelWidth)] < triggerLevel // positive trigger direction
                && buffer[channelWidth*triggerChannel+readPointer] >= triggerLevel) || 
                (triggerDir && buffer[channelWidth*triggerChannel+((readPointer-1+channelWidth)%channelWidth)] > triggerLevel // negative trigger direciton
                && buffer[channelWidth*triggerChannel+readPointer] <= triggerLevel);
    } else if (triggerMode == 2){   // custom trigger
        return (customTriggered && readPointer == customTriggerPointer);
    }
    return false;
}

void Scope::doTrigger(){
    // iterate over the samples between the read and write pointers and check for / deal with triggers
    while (readPointer != writePointer){
        
        // if we are currently listening for a trigger
        if (triggerPrimed){
            
            // if we crossed the trigger threshold
            if (triggered()){
                
                // stop listening for a trigger
                triggerPrimed = false;
                triggerCollecting = true;
                
                // save the readpointer at the trigger point
                triggerPointer = (readPointer-xOffset+channelWidth)%channelWidth;
                
                triggerCount = frameWidth/2 - xOffset;
                autoTriggerCount = 0;
                
            } else {
                // auto triggering
                if (triggerMode == 0 && (autoTriggerCount++ > (frameWidth+holdOff))){
                    // it's been a whole frameWidth since we've found a trigger, so auto-trigger anyway
                    triggerPrimed = false;
                    triggerCollecting = true;
                    
                    // save the readpointer at the trigger point
                    triggerPointer = (readPointer-xOffset+channelWidth)%channelWidth;
                    
                    triggerCount = frameWidth/2 - xOffset;
                    autoTriggerCount = 0;
                }
            }
            
        } else if (triggerCollecting){
            
            // a trigger has been detected, and we are collecting the second half of the triggered frame
            if (--triggerCount > 0){
                
            } else {
                triggerCollecting = false;
                triggerWaiting = true;
                triggerCount = frameWidth/2 + holdOffSamples;
                
                // copy the previous to next frameWidth/2 samples into the outBuffer
                int startptr = (triggerPointer-frameWidth/2 + channelWidth)%channelWidth;
                int endptr = (triggerPointer+frameWidth/2 + channelWidth)%channelWidth;
                
                if (endptr > startptr){
                    for (int i=0; i<numChannels; i++){
                        std::copy(&buffer[channelWidth*i+startptr], &buffer[channelWidth*i+endptr], outBuffer.begin()+(i*frameWidth));
                    }
                } else {
                    for (int i=0; i<numChannels; i++){
                        std::copy(&buffer[channelWidth*i+startptr], &buffer[channelWidth*(i+1)], outBuffer.begin()+(i*frameWidth));
                        std::copy(&buffer[channelWidth*i], &buffer[channelWidth*i+endptr], outBuffer.begin()+((i+1)*frameWidth-endptr));
                    }
                }
                
                // the whole frame has been saved in outBuffer, so send it
                scheduleSendBufferTask();
                
            }
            
        } else if (triggerWaiting){
            
            // a trigger has completed, so wait half a framewidth before looking for another
            if (--triggerCount > 0){
                
            } else {
                triggerWaiting = false;
                triggerPrimed = true;
                customTriggered = false;
            }
            
        }
        
        // increment the read pointer
        readPointer = (readPointer+1)%channelWidth;
    }

}

void Scope::sendBuffer(){
    socket.send(&(outBuffer[0]), outBuffer.size()*sizeof(float));
}

void Scope::parseMessage(oscpkt::Message msg){
    if (msg.partialMatch("/scope-settings/")){
        int intArg;
        float floatArg;
        if (msg.match("/scope-settings/connected").popInt32(intArg).isOkNoMoreArgs()){
            if (connected == 0 && intArg == 1){
                start();
            } else if (connected == 1 && intArg == 0){
                stop();
            }
            connected = intArg;
        } else if (msg.match("/scope-settings/frameWidth").popInt32(intArg).isOkNoMoreArgs()){
            stop();
            frameWidth = intArg;
            start();
        } else if (msg.match("/scope-settings/triggerMode").popInt32(intArg).isOkNoMoreArgs()){
            triggerMode = intArg;
        } else if (msg.match("/scope-settings/triggerChannel").popInt32(intArg).isOkNoMoreArgs()){
            triggerChannel = intArg;
        } else if (msg.match("/scope-settings/triggerDir").popInt32(intArg).isOkNoMoreArgs()){
            triggerDir = intArg;
        } else if (msg.match("/scope-settings/triggerLevel").popFloat(floatArg).isOkNoMoreArgs()){
            triggerLevel = floatArg;
        } else if (msg.match("/scope-settings/xOffset").popInt32(intArg).isOkNoMoreArgs()){
            xOffset = intArg;
        } else if (msg.match("/scope-settings/upSampling").popInt32(intArg).isOkNoMoreArgs()){
            upSampling = intArg;
            holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
        } else if (msg.match("/scope-settings/downSampling").popInt32(intArg).isOkNoMoreArgs()){
            downSampling = intArg;
            holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
        } else if (msg.match("/scope-settings/holdOff").popFloat(floatArg).isOkNoMoreArgs()){
            holdOff = floatArg;
            holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
        }
    }
}