Skip to content
Snippets Groups Projects
ControlPanel.cpp 18.25 KiB
/*
    ------------------------------------------------------------------

    This file is part of the Open Ephys GUI
    Copyright (C) 2013 Open Ephys

    ------------------------------------------------------------------

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "ControlPanel.h"
#include "UIComponent.h"
#include <stdio.h>
#include <math.h>

PlayButton::PlayButton()
    : DrawableButton("PlayButton", DrawableButton::ImageFitted)
{

    DrawablePath normal, over, down;

    Path p;
    p.addTriangle(0.0f, 0.0f, 0.0f, 20.0f, 18.0f, 10.0f);
    normal.setPath(p);
    normal.setFill(Colours::lightgrey);
    normal.setStrokeThickness(0.0f);

    over.setPath(p);
    over.setFill(Colours::black);
    over.setStrokeFill(Colours::black);
    over.setStrokeThickness(5.0f);

    down.setPath(p);
    down.setFill(Colours::pink);
    down.setStrokeFill(Colours::pink);
    down.setStrokeThickness(5.0f);

    setImages(&normal, &over, &over);
    // setBackgroundColours(Colours::darkgrey, Colours::yellow);
    setClickingTogglesState(true);
    setTooltip("Start/stop acquisition");


}

PlayButton::~PlayButton()
{
}

RecordButton::RecordButton()
    : DrawableButton("RecordButton", DrawableButton::ImageFitted)
{

    DrawablePath normal, over, down;

    Path p;
    p.addEllipse(0.0,0.0,20.0,20.0);
    normal.setPath(p);
    normal.setFill(Colours::lightgrey);
    normal.setStrokeThickness(0.0f);

    over.setPath(p);
    over.setFill(Colours::black);
    over.setStrokeFill(Colours::black);
    over.setStrokeThickness(5.0f);

    setImages(&normal, &over, &over);
    //setBackgroundColours(Colours::darkgrey, Colours::red);
    setClickingTogglesState(true);
    setTooltip("Start/stop writing to disk");
}

RecordButton::~RecordButton()
{
}


CPUMeter::CPUMeter() : Label("CPU Meter","0.0"), cpu(0.0f), lastCpu(0.0f)
{

    font = Font("Small Text", 12, Font::plain);

    // MemoryInputStream mis(BinaryData::silkscreenserialized, BinaryData::silkscreenserializedSize, false);
    // Typeface::Ptr typeface = new CustomTypeface(mis);
    // font = Font(typeface);
    // font.setHeight(12);
}

CPUMeter::~CPUMeter()
{
}

void CPUMeter::updateCPU(float usage)
{
    lastCpu = cpu;
    cpu = usage;
}

void CPUMeter::paint(Graphics& g)
{
    g.fillAll(Colours::grey);

    g.setColour(Colours::yellow);
    g.fillRect(0.0f,0.0f,getWidth()*cpu,float(getHeight()));

    g.setColour(Colours::black);
    g.drawRect(0,0,getWidth(),getHeight(),1);

    g.setFont(font);
    g.drawSingleLineText("CPU",65,12);

}


DiskSpaceMeter::DiskSpaceMeter()

{

    font = Font("Small Text", 12, Font::plain);

    // MemoryInputStream mis(BinaryData::silkscreenserialized, BinaryData::silkscreenserializedSize, false);
    // Typeface::Ptr typeface = new CustomTypeface(mis);
    // font = Font(typeface);
    // font.setHeight(12);
}

DiskSpaceMeter::~DiskSpaceMeter()
{
}

void DiskSpaceMeter::updateDiskSpace(float percent)
{
    diskFree = percent;
}

void DiskSpaceMeter::paint(Graphics& g)
{

    g.fillAll(Colours::grey);

    g.setColour(Colours::lightgrey);
    if (diskFree > 0)
        g.fillRect(0.0f,0.0f,getWidth()*diskFree,float(getHeight()));

    g.setColour(Colours::black);
    g.drawRect(0,0,getWidth(),getHeight(),1);

    g.setFont(font);
    g.drawSingleLineText("DF",75,12);

}

Clock::Clock() : isRunning(false), isRecording(false)
{

    clockFont = Font("Default Light", 30, Font::plain);

    // MemoryInputStream mis(BinaryData::cpmonolightserialized, BinaryData::cpmonolightserializedSize, false);
    // Typeface::Ptr typeface = new CustomTypeface(mis);
    // clockFont = Font(typeface);
    // clockFont.setHeight(30);

    totalTime = 0;
    totalRecordTime = 0;
}

Clock::~Clock()
{
}


void Clock::paint(Graphics& g)
{
    g.fillAll(Colour(58,58,58));
    drawTime(g);
}

void Clock::drawTime(Graphics& g)
{

    if (isRunning)
    {
        int64 now = Time::currentTimeMillis();
        int64 diff = now - lastTime;
        totalTime += diff;

        if (isRecording)
        {
            totalRecordTime += diff;
        }

        lastTime = Time::currentTimeMillis();
    }

    int m;
    int s;

    if (isRecording)
    {
        g.setColour(Colours::red);
        m = floor(totalRecordTime/60000.0);
        s = floor((totalRecordTime - m*60000.0)/1000.0);

    }
    else
    {

        if (isRunning)
            g.setColour(Colours::yellow);
        else
            g.setColour(Colours::white);

        m = floor(totalTime/60000.0);
        s = floor((totalTime - m*60000.0)/1000.0);
    }

    String timeString = "";

    timeString += m;
    timeString += " min ";
    timeString += s;
    timeString += " s";

    g.setFont(clockFont);
    //g.setFont(30);
    g.drawText(timeString, 0, 0, getWidth(), getHeight(), Justification::left, false);

}

void Clock::start()
{
    if (!isRunning)
    {
        isRunning = true;
        lastTime = Time::currentTimeMillis();
    }
}

void Clock::resetRecordTime()
{
    totalRecordTime = 0;
}

void Clock::startRecording()
{
    if (!isRecording)
    {
        isRecording = true;
        start();
    }
}

void Clock::stop()
{
    if (isRunning)
    {
        isRunning = false;
        isRecording = false;
    }
}

void Clock::stopRecording()
{
    if (isRecording)
    {
        isRecording = false;
    }

}


ControlPanelButton::ControlPanelButton(ControlPanel* cp_) : cp(cp_)
{
    open = false;

}

ControlPanelButton::~ControlPanelButton()
{

}

void ControlPanelButton::paint(Graphics& g)
{
    g.fillAll(Colour(58,58,58));

    g.setColour(Colours::white);

    Path p;

    float h = getHeight();
    float w = getWidth();

    if (open)
    {
        p.addTriangle(0.5f*w, 0.8f*h,
                      0.2f*w, 0.2f*h,
                      0.8f*w, 0.2f*h);
    }
    else
    {
        p.addTriangle(0.8f*w, 0.8f*h,
                      0.2f*w, 0.5f*h,
                      0.8f*w, 0.2f*h);
    }

    PathStrokeType pst = PathStrokeType(1.0f, PathStrokeType::curved, PathStrokeType::rounded);

    g.strokePath(p, pst);

}


void ControlPanelButton::mouseDown(const MouseEvent& e)
{
    open = !open;
    cp->openState(open);
    repaint();

}

void ControlPanelButton::toggleState()
{
    open = !open;
    repaint();
}

void ControlPanelButton::setState(bool b)
{
    open = b;
    repaint();
}



ControlPanel::ControlPanel(ProcessorGraph* graph_, AudioComponent* audio_)
    : graph(graph_), audio(audio_), initialize(true), open(false)
{

    if (1)
    {

        font = Font("Paragraph", 13, Font::plain);

        // MemoryInputStream mis(BinaryData::misoserialized, BinaryData::misoserializedSize, false);
        // Typeface::Ptr typeface = new CustomTypeface(mis);
        // font = Font(typeface);
        // font.setHeight(15);
    }

    audioEditor = (AudioEditor*) graph->getAudioNode()->createEditor();
    addAndMakeVisible(audioEditor);

    playButton = new PlayButton();
    playButton->addListener(this);
    addAndMakeVisible(playButton);

    recordButton = new RecordButton();
    recordButton->addListener(this);
    addAndMakeVisible(recordButton);

    masterClock = new Clock();
    addAndMakeVisible(masterClock);

    cpuMeter = new CPUMeter();
    addAndMakeVisible(cpuMeter);

    diskMeter = new DiskSpaceMeter();
    addAndMakeVisible(diskMeter);

    cpb = new ControlPanelButton(this);
    addAndMakeVisible(cpb);

    newDirectoryButton = new UtilityButton("+", Font("Small Text", 15, Font::plain));
    newDirectoryButton->setEnabledState(false);
    newDirectoryButton->addListener(this);
    addChildComponent(newDirectoryButton);



    filenameComponent = new FilenameComponent("folder selector",
                                              File::getCurrentWorkingDirectory().getFullPathName(),
                                              true,
                                              true,
                                              true,
                                              "*",
                                              "",
                                              "");
    addChildComponent(filenameComponent);

    prependText = new Label("Prepend","");
    prependText->setEditable(true);
    prependText->addListener(this);
    prependText->setColour(Label::backgroundColourId, Colours::lightgrey);

    addChildComponent(prependText);

    dateText = new Label("Date","YY-MM-DD_HH-MM-SS");
    dateText->setColour(Label::backgroundColourId, Colours::lightgrey);
    addChildComponent(dateText);

    appendText = new Label("Append","");
    appendText->setEditable(true);
    appendText->addListener(this);
    appendText->setColour(Label::backgroundColourId, Colours::lightgrey);
    addChildComponent(appendText);

    //diskMeter->updateDiskSpace(graph->getRecordNode()->getFreeSpace());
    //diskMeter->repaint();
    //refreshMeters();
    startTimer(10);

    setWantsKeyboardFocus(true);

}

ControlPanel::~ControlPanel()
{

}

void ControlPanel::setRecordState(bool t)
{

    //MessageManager* mm = MessageManager::getInstance();

    recordButton->setToggleState(t, true);

}

void ControlPanel::updateChildComponents()
{

    filenameComponent->addListener(getProcessorGraph()->getRecordNode());
    getProcessorGraph()->getRecordNode()->filenameComponentChanged(filenameComponent);

}

void ControlPanel::createPaths()
{
    int w = 150;
    int h1 = 32;
    int h2 = 64;
    int indent = 5;

    p1.clear();
    p1.startNewSubPath(0, h1);
    p1.lineTo(w, h1);
    p1.lineTo(w + indent, h1 + indent);
    p1.lineTo(w + indent, h2 - indent);
    p1.lineTo(w + indent*2, h2);
    p1.lineTo(0, h2);
    p1.closeSubPath();

    p2.clear();
    p2.startNewSubPath(getWidth(), h2-indent);
    p2.lineTo(getWidth(), h2);
    p2.lineTo(getWidth()-indent, h2);
    p2.closeSubPath();

}

void ControlPanel::paint(Graphics& g)
{
    g.setColour(Colour(58,58,58));
    g.fillRect(0,0,getWidth(),getHeight());

    if (open)
    {
        g.setColour(Colours::black);
        g.fillPath(p1);
        g.fillPath(p2);
    }

}

void ControlPanel::resized()
{
    int w = getWidth();
    int h = 32; //getHeight();

    if (playButton != 0)
        playButton->setBounds(w-h*9,5,h-5,h-10);

    if (recordButton != 0)
        recordButton->setBounds(w-h*8,5,h-5,h-10);

    if (masterClock != 0)
        masterClock->setBounds(w-h*6-15,0,h*6-15,h);

    if (cpuMeter != 0)
        cpuMeter->setBounds(8,h/4,h*3,h/2);

    if (diskMeter != 0)
        diskMeter->setBounds(16+h*3,h/4,h*3,h/2);

    if (audioEditor != 0)
        audioEditor->setBounds(h*7,5,h*8,h-10);

    if (cpb != 0)
        cpb->setBounds(w-28,5,h-10,h-10);

    createPaths();

    if (open)
    {
        filenameComponent->setBounds(165, h+5, w-500, h-10);
        filenameComponent->setVisible(true);

        newDirectoryButton->setBounds(w-h+4, h+5, h-10, h-10);
        newDirectoryButton->setVisible(true);

        prependText->setBounds(165+w-490, h+5, 50, h-10);
        prependText->setVisible(true);

        dateText->setBounds(165+w-435, h+5, 175, h-10);
        dateText->setVisible(true);

        appendText->setBounds(165+w-255, h+5, 50, h-10);
        appendText->setVisible(true);

    }
    else
    {
        filenameComponent->setVisible(false);
        newDirectoryButton->setVisible(false);
        prependText->setVisible(false);
        dateText->setVisible(false);
        appendText->setVisible(false);
    }

    repaint();
}

void ControlPanel::openState(bool os)
{
    open = os;

    cpb->setState(os);

    getUIComponent()->childComponentChanged();
}

void ControlPanel::labelTextChanged(Label* label)
{

}

void ControlPanel::buttonClicked(Button* button)

{
    
    //const MessageManagerLock mmLock;
    
    if (button == recordButton)
    {
        std::cout << "Record button pressed." << std::endl;
        if (recordButton->getToggleState())
        {

            playButton->setToggleState(true,false);
            masterClock->startRecording(); // turn on recording


        }
        else
        {
            graph->setRecordState(false); // turn off recording in processor graph
            //graph->getRecordNode()->setParameter(0,10.0f); // turn off recording
            masterClock->stopRecording();
            newDirectoryButton->setEnabledState(true);
        }

    }
    else if (button == playButton)
    {
        std::cout << "Play button pressed." << std::endl;
        if (!playButton->getToggleState())
        {
            if (recordButton->getToggleState())
            {
                recordButton->setToggleState(false,false);
                newDirectoryButton->setEnabledState(true);
            }

        }

    }
    else if (button == newDirectoryButton && newDirectoryButton->getEnabledState())
    {
        graph->getRecordNode()->newDirectoryNeeded = true;
        newDirectoryButton->setEnabledState(false);
        masterClock->resetRecordTime();
        return;

    }

    if (playButton->getToggleState())
    {

        if (!audio->callbacksAreActive())
        {

            if (graph->enableProcessors())
            {
                if (recordButton->getToggleState())
                    graph->setRecordState(true);
                    
                    
                    //graph->getRecordNode()->setParameter(1,10.0f);

                stopTimer();
                startTimer(250); // refresh every 250 ms
                audio->beginCallbacks();
                masterClock->start();
            }

        }
        else
        {

            if (recordButton->getToggleState())
            {
                graph->setRecordState(true); //getRecordNode()->setParameter(1,10.0f);
            }

        }

    }
    else
    {

        if (audio->callbacksAreActive())
        {
            audio->endCallbacks();
            graph->disableProcessors();
            refreshMeters();
            masterClock->stop();
            stopTimer();
            startTimer(10000); // back to refresh every 10 seconds

        }

    }

}

void ControlPanel::disableCallbacks()
{

    std::cout << "Control panel received signal to disable callbacks." << std::endl;

    if (audio->callbacksAreActive())
    {
        std::cout << "Stopping audio." << std::endl;
        audio->endCallbacks();
        std::cout << "Disabling processors." << std::endl;
        graph->disableProcessors();
        std::cout << "Updating control panel." << std::endl;
        refreshMeters();
        stopTimer();
        startTimer(10000); // back to refresh every 10 seconds

    }

    playButton->setToggleState(false,false);
    recordButton->setToggleState(false,false);
    masterClock->stopRecording();
    masterClock->stop();


}

// void ControlPanel::actionListenerCallback(const String & msg)
// {
// 	//std::cout << "Message Received." << std::endl;
// 	if (playButton->getToggleState()) {
// 		cpuMeter->updateCPU(audio->deviceManager.getCpuUsage());
// 	}

// 	cpuMeter->repaint();

// 	diskMeter->updateDiskSpace(graph->getRecordNode()->getFreeSpace());
// 	diskMeter->repaint();

// }

void ControlPanel::timerCallback()
{
    //std::cout << "Message Received." << std::endl;

    refreshMeters();

}

void ControlPanel::refreshMeters()
{
    if (playButton->getToggleState())
    {
        cpuMeter->updateCPU(audio->deviceManager.getCpuUsage());
    }
    else
    {
        cpuMeter->updateCPU(0.0f);
    }

    cpuMeter->repaint();

    masterClock->repaint();

    diskMeter->updateDiskSpace(graph->getRecordNode()->getFreeSpace());
    diskMeter->repaint();

    if (initialize)
    {
        stopTimer();
        startTimer(5000); // check for disk updates every 5 seconds
        initialize = false;
    }
}

bool ControlPanel::keyPressed(const KeyPress& key)
{
    std::cout << "Control panel received" << key.getKeyCode() << std::endl;

    return false;

}

void ControlPanel::toggleState()
{
    open = !open;

    cpb->toggleState();
    getUIComponent()->childComponentChanged();
}

String ControlPanel::getTextToAppend()
{
    String t = appendText->getText();

    if (t.length() > 0)
    {
        return "_" + t;
    }
    else
    {
        return t;
    }
}

String ControlPanel::getTextToPrepend()
{
    String t = prependText->getText();

    if (t.length() > 0)
    {
        return t + "_";
    }
    else
    {
        return t;
    }
}

void ControlPanel::setDateText(String t)
{
    dateText->setText(t, dontSendNotification);
}


void ControlPanel::saveStateToXml(XmlElement* xml)
{

    XmlElement* controlPanelState = xml->createNewChildElement("CONTROLPANEL");
    controlPanelState->setAttribute("isOpen",open);
    controlPanelState->setAttribute("prependText",prependText->getText());
    controlPanelState->setAttribute("appendText",appendText->getText());

}

void ControlPanel::loadStateFromXml(XmlElement* xml)
{

    forEachXmlChildElement(*xml, xmlNode)
    {
        if (xmlNode->hasTagName("CONTROLPANEL"))
        {

            appendText->setText(xmlNode->getStringAttribute("appendText", ""), dontSendNotification);
            prependText->setText(xmlNode->getStringAttribute("prependText", ""), dontSendNotification);

            bool isOpen = xmlNode->getBoolAttribute("isOpen");
            openState(isOpen);

        }
    }

}