Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Showing
with 2950 additions and 0 deletions
Copyright (c) 2016, Goldsmiths Creative Computing (UK, London)
All rights reserved.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Neither the name of Goldsmiths nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
/*
==============================================================================
This file was auto-generated!
It contains the basic startup code for a Juce application.
==============================================================================
*/
#include "../JuceLibraryCode/JuceHeader.h"
Component* createMainContentComponent();
//==============================================================================
class RAPID_JUCEApplication : public JUCEApplication
{
public:
//==============================================================================
RAPID_JUCEApplication() {}
const String getApplicationName() override { return ProjectInfo::projectName; }
const String getApplicationVersion() override { return ProjectInfo::versionString; }
bool moreThanOneInstanceAllowed() override { return true; }
//==============================================================================
void initialise (const String& commandLine) override
{
// This method is where you should put your application's initialisation code..
mainWindow = new MainWindow (getApplicationName());
}
void shutdown() override
{
// Add your application's shutdown code here..
mainWindow = nullptr; // (deletes our window)
}
//==============================================================================
void systemRequestedQuit() override
{
// This is called when the app is being asked to quit: you can ignore this
// request and let the app carry on running, or call quit() to allow the app to close.
quit();
}
void anotherInstanceStarted (const String& commandLine) override
{
// When another instance of the app is launched while this one is running,
// this method is invoked, and the commandLine parameter tells you what
// the other instance's command-line arguments were.
}
//==============================================================================
/*
This class implements the desktop window that contains an instance of
our MainContentComponent class.
*/
class MainWindow : public DocumentWindow
{
public:
MainWindow (String name) : DocumentWindow (name,
Colours::lightgrey,
DocumentWindow::allButtons)
{
setUsingNativeTitleBar (true);
setContentOwned (createMainContentComponent(), true);
setResizable (true, true);
centreWithSize (getWidth(), getHeight());
setVisible (true);
}
void closeButtonPressed() override
{
// This is called when the user tries to close this window. Here, we'll just
// ask the app to quit when this happens, but you can change this to do
// whatever you need.
JUCEApplication::getInstance()->systemRequestedQuit();
}
/* Note: Be careful if you override any DocumentWindow methods - the base
class uses a lot of them, so by overriding you might break its functionality.
It's best to do all your work in your content component instead, but if
you really have to override any DocumentWindow methods, make sure your
subclass also calls the superclass's method.
*/
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
};
private:
ScopedPointer<MainWindow> mainWindow;
};
//==============================================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (RAPID_JUCEApplication)
/*
==============================================================================
This file was auto-generated!
==============================================================================
*/
#ifndef MAINCOMPONENT_H_INCLUDED
#define MAINCOMPONENT_H_INCLUDED
// It's important to call any headers that might
// include apple libs / headers before Juce's
// to prevent ambigious classes
#include "../../../../dependencies/Maximilian/maximilian.h"
#include "../../../../dependencies/RapidLib/src/regression.h"
#include <random>
#include <array>
#include "../JuceLibraryCode/JuceHeader.h"
class MainContentComponent;
//==============================================================================
class Oscilloscope : public Component,
private Timer
{
public:
Oscilloscope(MainContentComponent* _p) : parent(_p),
writePosition(0),
bufferSize(16384),
paintSize(256)
{
startTimer(40);
}
void pushBuffer(const float* _sampleData, int _numSamples)
{
for (int i = 0; i < _numSamples; ++i)
{
size_t pos = ++writePosition % buffer.size();
oldBuffer[pos] = buffer[pos];
buffer[pos] = _sampleData[i];
}
}
void paint(Graphics& _g) override
{
boundary = getLocalBounds();
_g.fillAll(juce::Colour(0xff000000));
Path path;
path.startNewSubPath(0, .5 * boundary.getHeight());
const float bufferYScale = .3f;
int paintPos = 2;
while (paintPos < buffer.size())
{
if (isZeroCrossing(paintPos))
{
break;
}
++paintPos;
}
const int posOffset = paintPos;
while (paintPos < buffer.size())
{
float bufferPoint = buffer[paintPos];//0.5 * std::abs(buffer[paintPos] - oldBuffer[paintPos]);
Point<float> point((paintPos - posOffset) * boundary.getWidth() / paintSize,
0.5 * ((bufferYScale * bufferPoint) + 1) * boundary.getHeight());
path.lineTo(point);
++paintPos;
}
_g.setColour(juce::Colour(0xff00ff00));
_g.strokePath(path, PathStrokeType(2.0f));
}
void mouseDrag(const MouseEvent& _event) override;
private:
//==========================================================================
void timerCallback() override
{
repaint();
}
bool isZeroCrossing(int i) const noexcept
{
jassert(i > 0);
return buffer[i] > buffer[i - 1] && buffer[i] > 0 && buffer[i - 1] < 0;
}
//==========================================================================
juce::Rectangle<int> boundary;
std::array<float, int((float)44100 / 83)> buffer;
std::array<float, int((float)44100 / 83)> oldBuffer;
std::size_t writePosition;
const int bufferSize;
const int paintSize;
MainContentComponent* const parent;
};
//==============================================================================
class MainContentComponent : public AudioAppComponent,
public Slider::Listener,
public Button::Listener
{
public:
//==========================================================================
/* Setup */
MainContentComponent():
modulationFrequency(4.1),
modulationDepth(.2),
centreFrequency(512),
resonance(2),
targetModulationFrequency(4.1),
targetModulationDepth(.2),
targetCentreFrequency(512),
targetResonance(2),
trained(false),
oscilloscope(this)
{
header.setColour(TextButton::buttonColourId, Colours::wheat);
header.setColour(TextButton::textColourOffId, Colours::white);
header.setEnabled(false);
header.setButtonText("RAPID-MIX API Example");
addAndMakeVisible(header);
sidebar.setColour(TextButton::buttonColourId, Colours::white);
sidebar.setColour(TextButton::textColourOffId, Colours::white);
sidebar.setEnabled(false);
addAndMakeVisible(sidebar);
train.setColour(TextButton::buttonColourId, Colours::wheat);
train.setColour(TextButton::textColourOffId, Colours::white);
train.setButtonText("Train");
train.addListener(this);
addAndMakeVisible(train);
randomise.setColour(TextButton::buttonColourId, Colours::wheat);
randomise.setColour(TextButton::textColourOffId, Colours::white);
randomise.setButtonText("Randomise!");
randomise.addListener(this);
addAndMakeVisible(randomise);
footer.setColour(TextButton::buttonColourId, Colours::wheat);
footer.setColour(TextButton::textColourOffId, Colours::white);
footer.setEnabled(false);
footer.setButtonText("Select the synth parameters you like, then move the mouse to an area and hold space to associate that space with that sound. Repeat with a few sounds");
addAndMakeVisible(footer);
modulationFrequencySlider.setRange(0, 4096);
modulationFrequencySlider.setSkewFactorFromMidPoint (500.0);
modulationFrequencySlider.setValue(modulationFrequency, dontSendNotification);
modulationFrequencySlider.addListener(this);
addAndMakeVisible(modulationFrequencySlider);
modulationFrequencyLabel.setText("Mod Frequency", dontSendNotification);
modulationFrequencyLabel.setColour(Label::ColourIds::textColourId, Colours::white);
modulationFrequencyLabel.attachToComponent(&modulationFrequencySlider, false);
addAndMakeVisible(modulationFrequencyLabel);
modulationIndexSlider.setRange(0., 1.);
modulationIndexSlider.setValue(modulationDepth, dontSendNotification);
modulationIndexSlider.addListener(this);
addAndMakeVisible(modulationIndexSlider);
modulationIndexLabel.setText("Mod Index", dontSendNotification);
modulationFrequencyLabel.setColour(Label::ColourIds::textColourId, Colours::white);
modulationIndexLabel.attachToComponent(&modulationIndexSlider, false);
addAndMakeVisible(modulationIndexLabel);
filterFrequencySlider.setRange(0.00001, 4096);
filterFrequencySlider.setColour(Label::ColourIds::textColourId, Colours::white);
filterFrequencySlider.setSkewFactorFromMidPoint (500.0);
filterFrequencySlider.setValue(centreFrequency, dontSendNotification);
filterFrequencySlider.addListener(this);
addAndMakeVisible(filterFrequencySlider);
filterFrequencyLabel.setText("Filter Frequency", dontSendNotification);
filterFrequencyLabel.setColour(Label::ColourIds::textColourId, Colours::white);
filterFrequencyLabel.attachToComponent(&filterFrequencySlider, false);
addAndMakeVisible(filterFrequencyLabel);
filterResonanceSlider.setRange(0.00001, 40);
filterResonanceSlider.setValue(resonance, dontSendNotification);
filterResonanceSlider.addListener(this);
addAndMakeVisible(filterResonanceSlider);
filterResonanceLabel.setText("Filter Resonance", dontSendNotification);
filterResonanceLabel.setColour(Label::ColourIds::textColourId, Colours::white);
filterResonanceLabel.attachToComponent(&filterResonanceSlider, false);
addAndMakeVisible(filterResonanceLabel);
addAndMakeVisible(oscilloscope);
setSize (800, 600);
// No inputs, two outputs
setAudioChannels (0, 2);
}
~MainContentComponent()
{
shutdownAudio();
}
//==========================================================================
/* Audio Methods */
void prepareToPlay (int _samplesPerBlockExpected, double _sampleRate) override
{
// Remember this is part of the audio callback so we must be careful
// not to do anything silly like allocating memory or dare I say wait
// for locks!
maxiSettings::setup(std::round(_sampleRate), 2, _samplesPerBlockExpected);
}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
int periodLength = int(maxiSettings::sampleRate / 83);
// Get the buffers for both output channels
float* const bufferL = bufferToFill.buffer->getWritePointer(0, bufferToFill.startSample);
float* const bufferR = bufferToFill.buffer->getWritePointer(1, bufferToFill.startSample);
const double localTargetModulationFrequency = targetModulationFrequency;
const double localTargetModulationDepth = targetModulationDepth;
const double localTargetCentreFrequency = targetCentreFrequency;
const double localTargetResonance = targetResonance;
const double modulationFrequencyDelta = (localTargetModulationFrequency != modulationFrequency) ? (targetModulationFrequency - modulationFrequency) / bufferToFill.numSamples : 0;
const double modulationDepthDelta = (localTargetModulationDepth != modulationDepth) ? (targetModulationDepth - modulationDepth) / bufferToFill.numSamples : 0;
const double centreFrequencyDelta = (localTargetCentreFrequency != centreFrequency) ? (targetCentreFrequency - centreFrequency) / bufferToFill.numSamples : 0;
const double resonanceDelta = (localTargetResonance != resonance) ? (targetResonance - resonance) / bufferToFill.numSamples : 0;
// Create our PWM Maximilian noises and send the mixed output to the channels
for (int sample = 0; sample < bufferToFill.numSamples; ++sample)
{
modulationFrequency += modulationFrequencyDelta;
modulationDepth += modulationDepthDelta;
centreFrequency += centreFrequencyDelta;
resonance += resonanceDelta;
const double modulatorFrame = (modulator.sinewave(modulationFrequency) + 1.0) / 2.0 * modulationDepth + (1 - modulationDepth);
const double carrierFrame = carrier.pulse(83, modulatorFrame);
const double filteredFrame = filter.lores(carrierFrame, centreFrequency, resonance);
mixer.stereo(filteredFrame, outputs, .5);
bufferL[sample] = float(outputs[0]);
bufferR[sample] = float(outputs[1]);
}
modulationFrequency = localTargetModulationFrequency;
modulationDepth = localTargetModulationDepth;
centreFrequency = localTargetCentreFrequency;
resonance = localTargetResonance;
oscilloscope.pushBuffer(bufferL, bufferToFill.numSamples);
}
void releaseResources() override {}
//==========================================================================
/* Graphics, GUI */
void paint (Graphics& _g) override
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
_g.fillAll (Colour::fromRGB(40, 40, 40));
}
// This is called once at startup and then everytime the window is resized
// Essentially all the code here just subdivides the available window space
void resized() override
{
juce::Rectangle<int> area(getLocalBounds());
const int headerFooterHeight = 36;
header.setBounds(area.removeFromTop(headerFooterHeight));
footer.setBounds(area.removeFromBottom(headerFooterHeight));
const int sidebarWidth = jmax(100, area.getWidth() / 4);
juce::Rectangle<int> sidebarArea(area.removeFromLeft(sidebarWidth));
sidebar.setBounds(sidebarArea);
const int drawingWidth = area.getWidth();
drawingArea = area.removeFromRight(drawingWidth);
oscilloscope.setBounds(drawingArea.reduced(int(sidebarWidth / 4.0)));
const int sideItemHeight = 40;
const int sideItemMargin = 5;
train.setBounds(sidebarArea.removeFromTop(sideItemHeight).reduced(sideItemMargin));
randomise.setBounds(sidebarArea.removeFromBottom(sideItemHeight).reduced(sideItemMargin));
filterResonanceSlider.setBounds(sidebarArea.removeFromBottom(sideItemHeight).reduced(2.5*sideItemMargin));
filterFrequencySlider.setBounds(sidebarArea.removeFromBottom(sideItemHeight).reduced(2.5*sideItemMargin));
modulationIndexSlider.setBounds(sidebarArea.removeFromBottom(sideItemHeight).reduced(2.5*sideItemMargin));
modulationFrequencySlider.setBounds(sidebarArea.removeFromBottom(sideItemHeight).reduced(2.5*sideItemMargin));
}
//==========================================================================
/* Interaction events via mouse and keyboard */
void mouseDrag(const MouseEvent& _event) override
{
if (trained)
{
std::vector<double> input = normaliseMouseSpace(_event.position.roundToInt(), drawingArea);
std::vector<double> output = rapidRegression.run(input);
targetModulationFrequency = output[0];
targetModulationDepth = output[1];
targetCentreFrequency = output[2];
targetResonance = output[3];
modulationFrequencySlider.setValue(targetModulationFrequency, dontSendNotification);
modulationIndexSlider.setValue(targetModulationDepth, dontSendNotification);
filterFrequencySlider.setValue(targetCentreFrequency, dontSendNotification);
filterResonanceSlider.setValue(targetResonance, dontSendNotification);
}
else
{
AlertWindow::showMessageBoxAsync(AlertWindow::AlertIconType::WarningIcon, "Error", "Please train the model before trying to run it!", "ok");
}
}
void mouseMove(const MouseEvent& _event) override
{
if (KeyPress::isKeyCurrentlyDown(KeyPress::spaceKey))
{
std::vector<double> input = normaliseMouseSpace(_event.position.roundToInt(), drawingArea);
trainingExample example;
example.input = { input[0], input[1] };
example.output = { modulationFrequency, modulationDepth, centreFrequency, resonance };
trainingSet.push_back(example);
// only do once...
if (input.size() > 0)
{
footer.setButtonText("When you have finished recorded the sounds you want, press train!");
}
}
}
std::vector<double> normaliseMouseSpace(const juce::Point<int>& _position, const juce::Rectangle<int>& _area)
{
juce::Point<int> pos = _area.getConstrainedPoint(_position);
std::vector<double> temp;
temp.resize(2);
temp[0] = double(double(pos.getX() - _area.getX()) / _area.getWidth());
temp[1] = double(double(pos.getY() - _area.getY()) / _area.getHeight());
return temp;
}
void sliderValueChanged(Slider* _slider) override
{
if (_slider == &modulationFrequencySlider)
targetModulationFrequency = modulationFrequencySlider.getValue();
else if (_slider == &modulationIndexSlider)
targetModulationDepth = modulationIndexSlider.getValue();
else if (_slider == &filterFrequencySlider)
targetCentreFrequency = filterFrequencySlider.getValue();
else if (_slider == &filterResonanceSlider)
targetResonance = filterResonanceSlider.getValue();
}
void buttonClicked(Button* _button) override
{
if (_button == &train)
{
if (trainingSet.size() > 2)
{
trained = rapidRegression.train(trainingSet);
footer.setButtonText("Now just drag the mouse to play!");
}
else
{
AlertWindow::showMessageBoxAsync(AlertWindow::AlertIconType::WarningIcon, "Error", "Please record more audio and mouse coordinate examples before training!\n\nYou can add examples by choosing mouse coordinates with the current sound and holding space whilst wiggling the mouse", "ok");
}
}
else if (_button == &randomise)
{
std::random_device random;
std::default_random_engine generator(random());
std::uniform_real_distribution<double> distribution(0.0, 1.0);
targetModulationFrequency = 4096 * distribution(generator);
targetModulationDepth = distribution(generator);
targetCentreFrequency = 4096 * distribution(generator);
targetResonance = 40 * distribution(generator);
modulationFrequencySlider.setValue(targetModulationFrequency, dontSendNotification);
modulationIndexSlider.setValue(targetModulationDepth, dontSendNotification);
filterFrequencySlider.setValue(targetCentreFrequency, dontSendNotification);
filterResonanceSlider.setValue(targetResonance, dontSendNotification);
}
}
private:
//==========================================================================
/*** AUDIO ***/
// FM Synthesis private parameters
double modulationFrequency;
double modulationDepth;
double centreFrequency;
double resonance;
double targetModulationFrequency;
double targetModulationDepth;
double targetCentreFrequency;
double targetResonance;
double outputs[2];
// Maximilian objects
maxiOsc carrier;
maxiOsc modulator;
maxiFilter filter;
maxiMix mixer;
//==========================================================================
/*** MACHINE LEARNING ***/
// Rapid regression
regression rapidRegression;
std::vector<trainingExample> trainingSet;
// Program state
bool trained;
//==========================================================================
/*** GUI ***/
// Layout
TextButton header;
TextButton sidebar;
TextButton footer;
juce::Rectangle<int> drawingArea;
// Rapidmix
TextButton train;
// Maximilian
Slider modulationFrequencySlider;
Label modulationFrequencyLabel;
Slider modulationIndexSlider;
Label modulationIndexLabel;
Slider filterFrequencySlider;
Label filterFrequencyLabel;
Slider filterResonanceSlider;
Label filterResonanceLabel;
TextButton randomise;
//==========================================================================
/*** GRAPHICS ***/
Oscilloscope oscilloscope;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};
void Oscilloscope::mouseDrag(const MouseEvent& _event)
{
parent->mouseDrag(_event);
}
// (This function is called by the app startup code to create our main component)
Component* createMainContentComponent() { return new MainContentComponent(); }
#endif // MAINCOMPONENT_H_INCLUDED
<?xml version="1.0" encoding="UTF-8"?>
<JUCERPROJECT id="Zh7kpS" name="regression_synth" projectType="guiapp" version="1.0.0"
bundleIdentifier="com.goldsmiths.rapidmix" includeBinaryInAppConfig="1"
jucerVersion="5.2.0" displaySplashScreen="1" reportAppUsage="1"
splashScreenColour="Dark" cppLanguageStandard="11" companyWebsite="rapidmixapi.com"
companyCopyright="">
<MAINGROUP id="g6MNSM" name="regression_synth">
<GROUP id="{EEA5B7C1-BCCC-64B0-259E-056090611E46}" name="Maximilian">
<GROUP id="{E20267E7-DDC5-EF6E-1EAE-C4B745764568}" name="libs">
<FILE id="h0SFJ1" name="fft.cpp" compile="1" resource="0" file="../../../dependencies/Maximilian/libs/fft.cpp"/>
<FILE id="pYguvb" name="fft.h" compile="0" resource="0" file="../../../dependencies/Maximilian/libs/fft.h"/>
<FILE id="QKNTAJ" name="maxiAtoms.cpp" compile="1" resource="0" file="../../../dependencies/Maximilian/libs/maxiAtoms.cpp"/>
<FILE id="FFoUBX" name="maxiAtoms.h" compile="0" resource="0" file="../../../dependencies/Maximilian/libs/maxiAtoms.h"/>
<FILE id="iwzAfI" name="maxiBark.cpp" compile="1" resource="0" file="../../../dependencies/Maximilian/libs/maxiBark.cpp"/>
<FILE id="yMdl88" name="maxiBark.h" compile="0" resource="0" file="../../../dependencies/Maximilian/libs/maxiBark.h"/>
<FILE id="xngR2q" name="maxiFFT.cpp" compile="1" resource="0" file="../../../dependencies/Maximilian/libs/maxiFFT.cpp"/>
<FILE id="A78B6e" name="maxiFFT.h" compile="0" resource="0" file="../../../dependencies/Maximilian/libs/maxiFFT.h"/>
<FILE id="I4uKZC" name="maxiGrains.cpp" compile="1" resource="0" file="../../../dependencies/Maximilian/libs/maxiGrains.cpp"/>
<FILE id="iWeaAV" name="maxiGrains.h" compile="0" resource="0" file="../../../dependencies/Maximilian/libs/maxiGrains.h"/>
<FILE id="Zhejq6" name="maxim.h" compile="0" resource="0" file="../../../dependencies/Maximilian/libs/maxim.h"/>
<FILE id="sYsKW9" name="maxiMFCC.cpp" compile="1" resource="0" file="../../../dependencies/Maximilian/libs/maxiMFCC.cpp"/>
<FILE id="mCQV3j" name="maxiMFCC.h" compile="0" resource="0" file="../../../dependencies/Maximilian/libs/maxiMFCC.h"/>
<FILE id="u3TFe0" name="sineTable.h" compile="0" resource="0" file="../../../dependencies/Maximilian/libs/sineTable.h"/>
</GROUP>
<FILE id="m2Fbmi" name="maximilian.h" compile="0" resource="0" file="../../../dependencies/Maximilian/maximilian.h"/>
<FILE id="R5ZjoZ" name="maximilian.cpp" compile="1" resource="0" file="../../../dependencies/Maximilian/maximilian.cpp"/>
</GROUP>
<GROUP id="{D5573091-5DD2-23C2-72F7-033E80306961}" name="RapidLib">
<GROUP id="{9F227387-ED1A-B17B-AAF3-0B2D43546269}" name="src">
<GROUP id="{A3EB9FB3-AE6F-6CA1-169C-0FF061BD4B0C}" name="emscripten">
<FILE id="o0rrsi" name="classificationEmbindings.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/src/emscripten/classificationEmbindings.h"/>
<FILE id="K95sXr" name="knnEmbindings.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/emscripten/knnEmbindings.h"/>
<FILE id="BMqYkg" name="modelSetEmbindings.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/src/emscripten/modelSetEmbindings.h"/>
<FILE id="jEjfUj" name="nnEmbindings.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/emscripten/nnEmbindings.h"/>
<FILE id="gjnhIh" name="nodeEnv.js" compile="0" resource="1" file="../../../dependencies/RapidLib/src/emscripten/nodeEnv.js"/>
<FILE id="iRFc6d" name="rapidLibPost.js" compile="0" resource="1" file="../../../dependencies/RapidLib/src/emscripten/rapidLibPost.js"/>
<FILE id="PzNivx" name="rapidStreamEmbindings.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/src/emscripten/rapidStreamEmbindings.h"/>
<FILE id="GHQaRa" name="regressionEmbindings.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/src/emscripten/regressionEmbindings.h"/>
<FILE id="PivhZh" name="seriesClassificationEmbindings.h" compile="0"
resource="0" file="../../../dependencies/RapidLib/src/emscripten/seriesClassificationEmbindings.h"/>
<FILE id="Q4hJHG" name="svmEmbindings.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/emscripten/svmEmbindings.h"/>
</GROUP>
<FILE id="VDnAfe" name="baseModel.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/baseModel.h"/>
<FILE id="x2NkmO" name="classification.cpp" compile="1" resource="1"
file="../../../dependencies/RapidLib/src/classification.cpp"/>
<FILE id="iQdC88" name="classification.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/src/classification.h"/>
<FILE id="otvyPe" name="dtw.cpp" compile="1" resource="0" file="../../../dependencies/RapidLib/src/dtw.cpp"/>
<FILE id="zh6JH2" name="dtw.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/dtw.h"/>
<FILE id="gg80D5" name="fastDTW.cpp" compile="1" resource="0" file="../../../dependencies/RapidLib/src/fastDTW.cpp"/>
<FILE id="Ga8ett" name="fastDTW.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/fastDTW.h"/>
<FILE id="d3LgeL" name="knnClassification.cpp" compile="1" resource="0"
file="../../../dependencies/RapidLib/src/knnClassification.cpp"/>
<FILE id="G5zqzw" name="knnClassification.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/src/knnClassification.h"/>
<FILE id="eEEUwv" name="modelSet.cpp" compile="1" resource="0" file="../../../dependencies/RapidLib/src/modelSet.cpp"/>
<FILE id="ieo9qs" name="modelSet.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/modelSet.h"/>
<FILE id="BLEyMT" name="neuralNetwork.cpp" compile="1" resource="0"
file="../../../dependencies/RapidLib/src/neuralNetwork.cpp"/>
<FILE id="MEILfO" name="neuralNetwork.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/neuralNetwork.h"/>
<FILE id="x3ZMq1" name="rapidStream.cpp" compile="1" resource="0" file="../../../dependencies/RapidLib/src/rapidStream.cpp"/>
<FILE id="C7Nz0H" name="rapidStream.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/rapidStream.h"/>
<FILE id="wJpnuO" name="regression.cpp" compile="1" resource="0" file="../../../dependencies/RapidLib/src/regression.cpp"/>
<FILE id="ny9hho" name="regression.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/regression.h"/>
<FILE id="QwAPCf" name="searchWindow.cpp" compile="1" resource="0"
file="../../../dependencies/RapidLib/src/searchWindow.cpp"/>
<FILE id="hRP4uS" name="searchWindow.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/searchWindow.h"/>
<FILE id="INKhcY" name="seriesClassification.cpp" compile="1" resource="0"
file="../../../dependencies/RapidLib/src/seriesClassification.cpp"/>
<FILE id="B0c8F3" name="seriesClassification.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/src/seriesClassification.h"/>
<FILE id="T74lKl" name="svmClassification.cpp" compile="1" resource="0"
file="../../../dependencies/RapidLib/src/svmClassification.cpp"/>
<FILE id="cVqdZA" name="svmClassification.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/src/svmClassification.h"/>
<FILE id="lKTpTj" name="trainingExample.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/src/trainingExample.h"/>
<FILE id="DtjqOn" name="warpPath.cpp" compile="1" resource="0" file="../../../dependencies/RapidLib/src/warpPath.cpp"/>
<FILE id="JjHvke" name="warpPath.h" compile="0" resource="0" file="../../../dependencies/RapidLib/src/warpPath.h"/>
</GROUP>
<GROUP id="{C5C7E07C-213F-8ECA-B1B6-B6BE04D35EAF}" name="dependencies">
<GROUP id="{E2AB2F69-E9EC-0A7F-8492-228002C6DB3D}" name="json">
<FILE id="vuXt8e" name="json-forwards.h" compile="0" resource="0" file="../../../dependencies/RapidLib/dependencies/json/json-forwards.h"/>
<FILE id="pVNxcf" name="json.h" compile="0" resource="0" file="../../../dependencies/RapidLib/dependencies/json/json.h"/>
</GROUP>
<GROUP id="{5C2C9ACC-885E-978B-0183-576F0DD3DA61}" name="libsvm">
<FILE id="Hh1DiU" name="libsvm.cpp" compile="1" resource="0" file="../../../dependencies/RapidLib/dependencies/libsvm/libsvm.cpp"/>
<FILE id="f5D4KB" name="libsvm.h" compile="0" resource="0" file="../../../dependencies/RapidLib/dependencies/libsvm/libsvm.h"/>
</GROUP>
<GROUP id="{655D6BCB-6FC6-1A2E-B6D7-C9170D46D415}" name="src">
<FILE id="YiXjKj" name="BayesianFilter.cpp" compile="1" resource="0"
file="../../../dependencies/RapidLib/dependencies/bayesfilter/src/BayesianFilter.cpp"/>
<FILE id="Y6sxtQ" name="BayesianFilter.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/dependencies/bayesfilter/src/BayesianFilter.h"/>
<FILE id="z3J9mx" name="filter_utilities.cpp" compile="1" resource="0"
file="../../../dependencies/RapidLib/dependencies/bayesfilter/src/filter_utilities.cpp"/>
<FILE id="GT07wW" name="filter_utilities.h" compile="0" resource="0"
file="../../../dependencies/RapidLib/dependencies/bayesfilter/src/filter_utilities.h"/>
</GROUP>
<FILE id="ibbler" name="jsoncpp.cpp" compile="1" resource="0" file="../../../dependencies/RapidLib/dependencies/jsoncpp.cpp"/>
</GROUP>
</GROUP>
<GROUP id="{24ED27E9-EF99-2FF0-3FAC-40EF82C0F046}" name="Source">
<FILE id="KK8sNt" name="MainComponent.cpp" compile="1" resource="0"
file="Source/MainComponent.cpp"/>
<FILE id="vn82oj" name="Main.cpp" compile="1" resource="0" file="Source/Main.cpp"/>
</GROUP>
</MAINGROUP>
<EXPORTFORMATS>
<VS2015 targetFolder="Builds/VisualStudio2015">
<CONFIGURATIONS>
<CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="32-bit"
isDebug="1" optimisation="1" targetName="regression_synth" headerPath="../../../../dependencies/Maximilian/"
debugInformationFormat="ProgramDatabase" enablePluginBinaryCopyStep="0"/>
<CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="32-bit"
isDebug="0" optimisation="3" targetName="regression_synth" headerPath="../../../../dependencies/Maximilian"
debugInformationFormat="ProgramDatabase" enablePluginBinaryCopyStep="0"
linkTimeOptimisation="1"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_core" path="../JUCE/modules"/>
<MODULEPATH id="juce_events" path="../JUCE/modules"/>
<MODULEPATH id="juce_graphics" path="../JUCE/modules"/>
<MODULEPATH id="juce_data_structures" path="../JUCE/modules"/>
<MODULEPATH id="juce_gui_basics" path="../JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../JUCE/modules"/>
<MODULEPATH id="juce_cryptography" path="../JUCE/modules"/>
<MODULEPATH id="juce_video" path="../JUCE/modules"/>
<MODULEPATH id="juce_opengl" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_basics" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_devices" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_formats" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_processors" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_utils" path="../JUCE/modules"/>
</MODULEPATHS>
</VS2015>
<XCODE_MAC targetFolder="Builds/MacOSX">
<CONFIGURATIONS>
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="regression_synth"
headerPath="../../../../dependencies/Maximilian" enablePluginBinaryCopyStep="1"/>
<CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="regression_synth"
headerPath="../../../../dependencies/Maximilian" enablePluginBinaryCopyStep="1"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_video" path="../JUCE/modules"/>
<MODULEPATH id="juce_opengl" path="../JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../JUCE/modules"/>
<MODULEPATH id="juce_gui_basics" path="../JUCE/modules"/>
<MODULEPATH id="juce_graphics" path="../JUCE/modules"/>
<MODULEPATH id="juce_events" path="../JUCE/modules"/>
<MODULEPATH id="juce_data_structures" path="../JUCE/modules"/>
<MODULEPATH id="juce_cryptography" path="../JUCE/modules"/>
<MODULEPATH id="juce_core" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_utils" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_processors" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_formats" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_devices" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_basics" path="../JUCE/modules"/>
</MODULEPATHS>
</XCODE_MAC>
<LINUX_MAKE targetFolder="Builds/LinuxMakefile">
<CONFIGURATIONS>
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="regression_synth"/>
<CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="regression_synth"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_video" path="../JUCE/modules"/>
<MODULEPATH id="juce_opengl" path="../JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../JUCE/modules"/>
<MODULEPATH id="juce_gui_basics" path="../JUCE/modules"/>
<MODULEPATH id="juce_graphics" path="../JUCE/modules"/>
<MODULEPATH id="juce_events" path="../JUCE/modules"/>
<MODULEPATH id="juce_data_structures" path="../JUCE/modules"/>
<MODULEPATH id="juce_cryptography" path="../JUCE/modules"/>
<MODULEPATH id="juce_core" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_utils" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_processors" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_formats" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_devices" path="../JUCE/modules"/>
<MODULEPATH id="juce_audio_basics" path="../JUCE/modules"/>
</MODULEPATHS>
</LINUX_MAKE>
</EXPORTFORMATS>
<MODULES>
<MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_cryptography" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_opengl" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_video" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
</MODULES>
<JUCEOPTIONS/>
<LIVE_SETTINGS>
<WINDOWS/>
</LIVE_SETTINGS>
</JUCERPROJECT>
# BitalinoRapidMix
Implementation of Bitalino EMG and a regression model powered by RapidMix in openframeworks with maximillian for audio for interesting synthesizer control / ( recursive ) mapping.
You can find the machine learning parts in `BITSequencerThread.cpp` and `RapidMixThread.h`
//
// BarChart.hpp
// RapidVisualizerOSC
//
// Created by James on 09/11/2017.
//
//
#ifndef BarChart_hpp
#define BarChart_hpp
#include <stdio.h>
#include "RealTimeGraph.hpp"
class BarChart : public RealTimeGraph
{
public:
BarChart ( GraphState* state ) : RealTimeGraph (state)
{
//
}
~BarChart ( void )
{
//
}
void updateRep ( void )
{
//
}
void drawSubGraph ( std::string subLabel, DataContainer<std::vector<double>>& data, ofRectangle subLabelRect )
{
double
minIn = 0,
minOut = 0,
maxOut = -subLabelRect.height,
startPosY = subLabelRect.height,
barSize = subLabelRect.width/data.labelData.size(),
separation = barSize/16,
halfSeparation = separation/2;
bool drawZeroSep = false;
if (data.minValue < 0)
{ // Add negative part
startPosY = subLabelRect.height/2;
minIn = -data.maxValue;
minOut = subLabelRect.height/2;
maxOut /= 2;
drawZeroSep = true;
}
for (uint32_t i = 0; i < data.labelData.size(); ++i)
{
double output = mapVals(data.labelData[i], minIn, data.maxValue, minOut, maxOut );
ofSetColor (graphColor);
ofFill();
ofDrawRectangle(subLabelRect.x + barSize * i + halfSeparation, subLabelRect.y + startPosY, barSize - separation, output );
}
if (drawZeroSep)
{
ofSetLineWidth(1.25);
ofSetColor (175,150,150);
ofDrawLine(subLabelRect.x, subLabelRect.y + startPosY,
subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
}
}
void update ( void )
{
//
}
};
#endif /* BarChart_hpp */
//
// Graph.hpp
// RapidVisualizerOSC
//
// Created by James on 09/11/2017.
//
//
#ifndef Graph_h
#define Graph_h
#include <string>
#include <vector>
#include <list>
#include "ofMain.h"
#include "ofxGui.h"
// TODO: add namespace, move funcs and struct to other header(s)
enum class TextAlignment {
LEFT,
CENTER,
RIGHT
};
static inline void drawTextLabel ( std::string label, ofVec2f position, ofColor labelBackgroundColor, ofColor stringColor, TextAlignment alignment, bool drawAbove )
{
uint32_t strLenPix = label.length()*8;
switch (alignment) {
case TextAlignment::LEFT:
// No exception
break;
case TextAlignment::CENTER:
position.x -= strLenPix / 2;
break;
case TextAlignment::RIGHT:
position.x -= strLenPix;
break;
default:
break;
}
ofSetColor (labelBackgroundColor);
ofRectangle bmpStringRect(position.x - 2,
position.y + ((drawAbove) ? -4 : 12) + 2,
strLenPix + 4, -12);
ofDrawRectangle(bmpStringRect);
ofSetColor (stringColor);
ofDrawBitmapString(label, position.x, position.y + ((drawAbove) ? -4 : 12));
}
static inline double mapVals ( double x, double in_min, double in_max, double out_min, double out_max )
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
template <typename T>
struct DataContainer {
T labelData;
double minValue = 0.0;
double maxValue = 1.0;
uint32_t iteratorPos = 0;
//ofColor graphColor;
void updateMinMax ( void )
{
auto mm = std::minmax_element(labelData.begin(), labelData.end());
double min = labelData[std::distance(labelData.begin(), mm.first)];
double max = labelData[std::distance(labelData.begin(), mm.second)];
if (min < minValue)
minValue = min;
if (max > maxValue)
maxValue = max;
if (fabs(min) > maxValue)
maxValue = fabs(min);
}
};
class Graph {
public:
enum graphLayout {
GRAPHS_STACKED,
GRAPHS_VERTICAL
};
struct GraphState {
std::string label;
graphLayout layout = graphLayout::GRAPHS_STACKED;
bool hasHistory = false;
ofRectangle positionAndSize;
uint32_t historySize;
};
Graph ( GraphState* state )
: state(state)
{
graphColor = ofColor(24, 219, 92);
textColor = ofColor(255, 157, 117);
}
virtual ~Graph ( void )
{
}
virtual void updateRep ( void ) = 0; // update representation
virtual void addData ( std::string subLabel, std::vector<double>& data ) = 0;
virtual void reset ( void ) = 0;
virtual void update ( void ) = 0;
virtual void draw ( void ) = 0;
virtual uint32_t getNumSubGraphs ( void ) const = 0;
protected:
GraphState *state = nullptr;
ofColor graphColor;
ofColor textColor;
};
#endif /* Graph_h */
//
// HistoryGraph.hpp
// RapidVisualizerOSC
//
// Created by James on 09/11/2017.
//
//
#ifndef HistoryGraph_h
#define HistoryGraph_h
#include "Graph.hpp"
#include <list>
class HistoryGraph : public Graph
{
public:
HistoryGraph ( GraphState* state )
: Graph(state)
{
}
~HistoryGraph ( void )
{
}
virtual void updateRep ( void ) = 0; // update representation
virtual void drawSubGraph ( std::string subLabel, DataContainer<std::list<double>>& data, ofRectangle subLabelrect ) = 0;
virtual void update ( void ) = 0;
void addData ( std::string subLabel, std::vector<double>& data )
{
if (data.size() < state->historySize)
{
//FIXME: can be sped up by using the result of this search instead of accessing by[] again
if ( subLabelData.find(subLabel) == subLabelData.end() ) {
// not found
DataContainer<std::list<double>> container;
container.labelData.resize(state->historySize);
std::fill(container.labelData.begin(), container.labelData.end(), 0.0);
subLabelData[subLabel] = container;
}
DataContainer<std::list<double>>& dataRef = subLabelData[subLabel];
list<double>& referencedList = dataRef.labelData;
while (referencedList.size() + data.size() >= state->historySize)
{
referencedList.pop_front();
}
for (int32_t i = data.size()-1; i >= 0; --i)
{
double val = data[i];
if (val < dataRef.minValue)
dataRef.minValue = val;
if (val > dataRef.maxValue)
dataRef.maxValue = val;
if (fabs(val) > dataRef.maxValue)
dataRef.maxValue = fabs(val);
referencedList.push_back(val);
}
}
}
void reset ( void )
{
subLabelData.clear();
}
void draw ( void )
{
uint32_t numElements = subLabelData.size();
uint16_t heightBetweenSubLabels = state->positionAndSize.height/numElements;
uint16_t subLabelY = 0;
ofSetColor (textColor);
ofDrawLine(state->positionAndSize.x, state->positionAndSize.y + heightBetweenSubLabels - 1.5,
state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y + heightBetweenSubLabels - 1.5);
std::map<std::string, DataContainer<std::list<double>>>::iterator it;
for(it = subLabelData.begin(); it != subLabelData.end(); ++it)
{
std::string subLabel = it->first;
DataContainer<std::list<double>>& data = it->second;
drawSubGraph (subLabel, data, ofRectangle(state->positionAndSize.x,
state->positionAndSize.y + subLabelY + 3,
state->positionAndSize.width,
heightBetweenSubLabels - 6));
// Draw label and background
drawTextLabel(subLabel, ofVec2f(state->positionAndSize.x + state->positionAndSize.width,
state->positionAndSize.y + subLabelY),
ofColor(100, 100, 100), ofColor(textColor), TextAlignment::RIGHT, false);
// Draw max value
drawTextLabel(ofToString(data.maxValue),
ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
state->positionAndSize.y + subLabelY),
ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, false);
// Increment Y position
subLabelY += heightBetweenSubLabels;
// Draw min value
// Show actual min value?
drawTextLabel(ofToString((data.minValue < 0) ? -data.maxValue : 0),
ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
state->positionAndSize.y + subLabelY),
ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, true);
// Draw Line at bottom of graph
//ofSetLineWidth(2.0);
//ofSetColor (180,180,180);
/*ofDrawLine(state->positionAndSize.x, state->positionAndSize.y + subLabelY + 3,
state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y + subLabelY + 3);*/
}
}
uint32_t getNumSubGraphs ( void ) const
{
return subLabelData.size();
}
protected:
std::map <std::string, DataContainer<std::list<double>>> subLabelData;
};
#endif /* HistoryGraph_h */
//
// LineGraph.hpp
// RapidVisualizerOSC
//
// Created by James on 09/11/2017.
//
//
#ifndef LineGraph_h
#define LineGraph_h
#include <stdio.h>
#include "RealTimeGraph.hpp"
class LineGraph : public RealTimeGraph
{
public:
LineGraph ( GraphState* state ) : RealTimeGraph (state)
{
//
}
~LineGraph ( void )
{
//
}
void updateRep ( void )
{
//
}
void drawSubGraph ( std::string subLabel, DataContainer<std::vector<double>>& data, ofRectangle subLabelRect )
{
double
minIn = 0,
minOut = 0,
maxOut = -subLabelRect.height,
startPosY = subLabelRect.height,
pointDistance = subLabelRect.width/data.labelData.size(),
separation = pointDistance/2;
//halfSeparation = separation/2;
bool drawZeroSep = false;
if (data.minValue < 0)
{ // Add negative part
startPosY = subLabelRect.height/2;
minIn = -data.maxValue;
minOut = subLabelRect.height/2;
maxOut /= 2;
drawZeroSep = true;
}
ofBeginShape();
ofFill();
ofVertex(subLabelRect.x, subLabelRect.y + startPosY);
double output = mapVals(data.labelData[0], minIn, data.maxValue, minOut, maxOut );
ofVertex(subLabelRect.x, subLabelRect.y + startPosY + output);
for (uint32_t i = 0; i < data.labelData.size(); ++i)
{
output = mapVals(data.labelData[i], minIn, data.maxValue, minOut, maxOut );
ofSetColor (graphColor);
ofVertex(subLabelRect.x + pointDistance * i + separation, subLabelRect.y + startPosY + output);
//ofDrawRectangle(subLabelRect.x + barSize * i + halfSeparation, subLabelRect.y + startPosY, barSize - separation, output );
}
output = mapVals(data.labelData[data.labelData.size()-1], minIn, data.maxValue, minOut, maxOut );
ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY + output);
ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
ofEndShape();
if (drawZeroSep)
{
ofSetLineWidth(1.25);
ofSetColor (175,150,150);
ofDrawLine(subLabelRect.x, subLabelRect.y + startPosY,
subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
}
}
void update ( void )
{
//
}
};
#endif /* LineGraph_h */
//
// LineGraphHistory.hpp
// RapidVisualizerOSC
//
// Created by James on 10/11/2017.
//
//
#ifndef LineGraphHistory_h
#define LineGraphHistory_h
#include <stdio.h>
#include "HistoryGraph.hpp"
class LineGraphHistory : public HistoryGraph
{
public:
LineGraphHistory ( GraphState* state ) : HistoryGraph (state)
{
//
}
~LineGraphHistory ( void )
{
//
}
void updateRep ( void )
{
//
}
void drawSubGraph ( std::string subLabel, DataContainer<std::list<double>>& data, ofRectangle subLabelRect )
{
double
minIn = 0,
minOut = 0,
maxOut = -subLabelRect.height,
startPosY = subLabelRect.height,
pointDistance = subLabelRect.width/data.labelData.size(),
separation = pointDistance/2;
//halfSeparation = separation/2;
bool drawZeroSep = false;
if (data.minValue < 0)
{ // Add negative part
startPosY = subLabelRect.height/2;
minIn = -data.maxValue;
minOut = subLabelRect.height/2;
maxOut /= 2;
drawZeroSep = true;
}
ofBeginShape();
ofFill();
ofVertex(subLabelRect.x, subLabelRect.y + startPosY);
double output = mapVals(data.labelData.front(), minIn, data.maxValue, minOut, maxOut );
ofVertex(subLabelRect.x, subLabelRect.y + startPosY + output);
uint32_t i = 0;
for (double d : data.labelData)
{
output = mapVals(d, minIn, data.maxValue, minOut, maxOut );
ofSetColor (graphColor);
ofVertex(subLabelRect.x + pointDistance * i + separation, subLabelRect.y + startPosY + output);
//ofDrawRectangle(subLabelRect.x + barSize * i + halfSeparation, subLabelRect.y + startPosY, barSize - separation, output );
++i;
}
output = mapVals(data.labelData.back(), minIn, data.maxValue, minOut, maxOut );
ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY + output);
ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
ofEndShape();
if (drawZeroSep)
{
ofSetLineWidth(1.25);
ofSetColor (175,150,150);
ofDrawLine(subLabelRect.x, subLabelRect.y + startPosY,
subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
}
}
void update ( void )
{
//
}
};
#endif /* LineGraphHistory_h */
//
// RapidVisualization.cpp
// RapidVisualizerOSC
//
// Created by James on 13/11/2017.
//
//
#include <stdio.h>
#include "RapidVisualization.hpp"
RapidVisualization::RapidVisualization ( void )
{
}
RapidVisualization::~RapidVisualization ( void )
{
std::map<std::string, RapidVisualizer*>::iterator it;
for(it = visualizers.begin(); it != visualizers.end(); ++it)
{
if (it->second)
delete it->second;
}
}
void RapidVisualization::setup ( ofRectangle posAndSize, uint32_t defaultHistorySize )
{
this->posAndSize = posAndSize;
this->defaultHistorySize = defaultHistorySize;
}
std::pair<std::string, std::string> RapidVisualization::getRoute(std::string& address)
{
std::pair<std::string, std::string> ret;
size_t delimPos = address.substr(1).find("/");
if (delimPos != std::string::npos)
{
delimPos += 1;
ret.first = address.substr(0, delimPos);
ret.second = address.substr(delimPos);
}
else
{
ret.first = address.substr(0, address.length());
ret.second = "/";
}
return ret;
}
void RapidVisualization::addData ( std::string address, std::vector<double>& data )
{
std::pair<std::string, std::string> route = getRoute(address);
if ( visualizers.find(route.first) == visualizers.end() ) {
RapidVisualizer* ptr = visualizers[route.first] = new RapidVisualizer(); // Add new graph
ptr->setup(route.first, RapidVisualizer::LINE_GRAPH_WITH_HISTORY, defaultHistorySize, Graph::GRAPHS_STACKED, false, ofRectangle(0,0,100,100));
}
RapidVisualizer* rapidVizPtr = visualizers[route.first];
rapidVizPtr->addData(address, data);
updateRep();
}
void RapidVisualization::addData ( std::string graphName, std::string subGraphName, std::vector<double>& data )
{
if ( visualizers.find(graphName) == visualizers.end() ) {
RapidVisualizer* ptr = visualizers[graphName] = new RapidVisualizer(); // Add new graph
ptr->setup(graphName, RapidVisualizer::LINE_GRAPH_WITH_HISTORY, defaultHistorySize, Graph::GRAPHS_STACKED, false, ofRectangle(0,0,100,100));
}
RapidVisualizer* rapidVizPtr = visualizers[graphName];
rapidVizPtr->addData(subGraphName, data);
updateRep();
}
void RapidVisualization::reset ( void )
{
std::map<std::string, RapidVisualizer*>::iterator it;
for(it = visualizers.begin(); it != visualizers.end(); ++it)
{
if (it->second)
delete it->second;
}
visualizers.clear();
}
void RapidVisualization::update ( void )
{
uint32_t tempNumGraphs = 0;
bool resetCase = false; // This is terrible (stops jitter when selecting new graph type)
std::map<std::string, RapidVisualizer*>::iterator it;
for(it = visualizers.begin(); it != visualizers.end(); ++it)
{
it->second->update();
uint32_t tGraph = it->second->getNumGraphs();
if (tGraph == 0)
{
resetCase = true;
break;
}
tempNumGraphs += tGraph;
}
if (tempNumGraphs != numGraphs && !resetCase)
{
numGraphs = tempNumGraphs;
updateRep();
}
}
void RapidVisualization::draw ( void )
{
std::map<std::string, RapidVisualizer*>::iterator it;
for(it = visualizers.begin(); it != visualizers.end(); ++it)
{
it->second->draw();
}
if (!guiHidden)
for(it = visualizers.begin(); it != visualizers.end(); ++it)
{
it->second->drawMenu(posAndSize); // Stop menu being behind next graph
}
if (numGraphs == 0)
{
drawTextLabel("Waiting for data", ofVec2f(posAndSize.x+posAndSize.width/2, posAndSize.y), ofColor(24, 219, 92), ofColor(255, 157, 117), TextAlignment::CENTER, false );
}
}
void RapidVisualization::setPos ( ofVec2f pos )
{
posAndSize.x = pos.x;
posAndSize.y = pos.y;
updateRep();
}
void RapidVisualization::setSize ( ofVec2f size )
{
posAndSize.width = size.x;
posAndSize.height = size.y;
updateRep();
}
void RapidVisualization::setPosAndSize ( ofRectangle posAndSize )
{
this->posAndSize = posAndSize;
updateRep();
}
void RapidVisualization::setHistorySize ( uint32_t size )
{
defaultHistorySize = size;
}
void RapidVisualization::updateRep ( void )
{
double graphHeight = posAndSize.height/numGraphs; // Height of single graph
std::map<std::string, RapidVisualizer*>::iterator it;
double drawHeight = posAndSize.y;
double height;
for(it = visualizers.begin(); it != visualizers.end(); ++it)
{
height = it->second->getNumGraphs()*graphHeight;
it->second->setRect(ofRectangle(posAndSize.x,
drawHeight,
posAndSize.width,
height));
drawHeight += height;
}
}
void RapidVisualization::setGuiHidden ( bool guiHidden )
{
this->guiHidden = guiHidden;
}
//
// RapidVisualization.hpp
// RapidVisualizerOSC
//
// Created by James on 10/11/2017.
//
//
#ifndef RapidVisualization_h
#define RapidVisualization_h
#include "RapidVisualizer.hpp"
class RapidVisualization
{
public:
RapidVisualization ( void );
~RapidVisualization ( void );
void setup ( ofRectangle posAndSize, uint32_t defaultHistorySize=256 );
// Append multiple types of graphs to cycle through as new entry addresses are added
// Implement?
//RapidVisualizer* addGraphType ( RapidVisualizer::graphTypeRealtime graphType );
//RapidVisualizer* addGraphType ( RapidVisualizer::graphTypeWithHistory graphType, uint32_t historySize );
void addData ( std::string address, std::vector<double>& data );
void addData ( std::string graphName, std::string subGraphName, std::vector<double>& data );
void reset ( void );
void update ( void );
void draw ( void );
void setPos ( ofVec2f pos );
void setSize ( ofVec2f size );
void setPosAndSize ( ofRectangle posAndSize );
void setHistorySize ( uint32_t size );
void setGraphColor ( ofColor graphColor );
//void setGraphColor ( ofColor backgroundColor );
//void setGraphColor ( ofColor textColor );
void setGuiHidden ( bool guiHidden );
private:
bool guiHidden = false;
uint32_t numGraphs = 0;
std::map<std::string, RapidVisualizer*> visualizers;
ofRectangle posAndSize;
uint32_t defaultHistorySize;
std::pair<std::string, std::string> getRoute ( std::string& address );
void updateRep ( void );
};
#endif /* RapidVisualization_h */
//
// RapidVisualizer.cpp
// RapidVisualizerOSC
//
// Created by James on 09/11/2017.
//
//
#include "RapidVisualizer.hpp"
RapidVisualizer::RapidVisualizer ( void )
{
uint32 initialHistorySize = 256;
minimumGui.setup();
expandedGui.setBackgroundColor(ofColor(0,0,0,127));
expandedGui.setup();
historySizeSlider.addListener(this, &RapidVisualizer::historySizeSliderChanged);
auto gNameRef = expandButton.setup(graphState.label, false);
expandButton.setBackgroundColor(ofColor(0, 0, 0, 127));
minimumGui.add(gNameRef);
expandedGui.add(graphTypesLabel.setup("Graph Type",""));
expandedGui.add(viewTypes[0].setup("Bar Chart", false));
expandedGui.add(viewTypes[1].setup("Line Graph", false));
expandedGui.add(viewTypes[2].setup("Line Graph History", false));
expandedGui.add(viewTypes[3].setup("Scope", false));
expandedGui.add(prefLabel.setup("Preferences",""));
expandedGui.add(splitArrayIntoGraphs.setup("Split Multiple Input", false));
expandedGui.add(historySizeSlider.setup("History Size", initialHistorySize, 4, 4096));
setHistory(initialHistorySize);
setLayout (Graph::GRAPHS_STACKED);
setSplitEachArgument (false);
setRect (ofRectangle(0,0,200,200));
}
RapidVisualizer::~RapidVisualizer ( void )
{
if (currentGraph)
{
delete currentGraph;
}
}
void RapidVisualizer::setup ( std::string label, graphTypeT graphType, Graph::graphLayout layout, bool splitEachArgument, ofRectangle positionAndSize )
{
setLabel (label);
setLayout (layout);
setSplitEachArgument (splitEachArgument);
setRect (positionAndSize);
setGraphType (graphType); // FIXME: Check if successfully initialized
}
void RapidVisualizer::setup ( std::string label, graphTypeT graphType, uint32_t historySize, Graph::graphLayout layout, bool splitEachArgument, ofRectangle positionAndSize )
{
setLabel (label);
setLayout (layout);
setSplitEachArgument (splitEachArgument);
setRect (positionAndSize);
setGraphType (graphType, historySize); // FIXME: Check if successfully initialized
}
void RapidVisualizer::setLabel ( std::string label )
{
graphState.label = label;
expandButton.setName(label);
if (currentGraph)
currentGraph->updateRep();
}
void RapidVisualizer::setGraphType ( graphTypeT graphType )
{
if (currentGraphType != graphType)
{
if (currentGraph)
{
// TODO: add lock for when doing this, or have all types loaded?
delete currentGraph;
}
switch (graphType) {
case BAR_CHART:
currentGraph = new BarChart ( &graphState );
graphState.hasHistory = false;
break;
case LINE_GRAPH:
currentGraph = new LineGraph ( &graphState );
graphState.hasHistory = false;
break;
case LINE_GRAPH_WITH_HISTORY:
currentGraph = new LineGraphHistory ( &graphState );
graphState.hasHistory = true;
break;
case SCOPE:
currentGraph = new ScopeWithHistory ( &graphState );
graphState.hasHistory = true;
break;
default:
break;
}
currentGraphType = graphType;
}
}
void RapidVisualizer::setGraphType ( graphTypeT graphType, uint32_t historySize )
{
setHistory(historySize);
setGraphType(graphType);
}
void RapidVisualizer::setLayout ( Graph::graphLayout layout )
{
graphState.layout = layout;
if (currentGraph)
currentGraph->updateRep();
}
void RapidVisualizer::setSplitEachArgument ( bool splitEachArgument )
{
this->splitEachArgument = splitEachArgument;
splitArrayIntoGraphs = splitEachArgument;
}
void RapidVisualizer::setPos ( ofVec2f pos )
{
graphState.positionAndSize.x = pos.x;
graphState.positionAndSize.y = pos.y;
if (currentGraph)
currentGraph->updateRep();
}
void RapidVisualizer::setSize ( ofVec2f size )
{
graphState.positionAndSize.width = size.x;
graphState.positionAndSize.height = size.y;
if (currentGraph)
currentGraph->updateRep();
}
void RapidVisualizer::setRect ( ofRectangle positionAndSize )
{
graphState.positionAndSize = positionAndSize;
if (currentGraph)
currentGraph->updateRep();
}
void RapidVisualizer::setHistory( uint32_t historySize )
{
graphState.historySize = historySize;
historySizeSlider = historySize;
if (currentGraph)
currentGraph->updateRep();
}
void RapidVisualizer::historySizeSliderChanged (int32_t &val)
{
setHistory(val);
}
void RapidVisualizer::addData ( std::string subLabel, std::vector<double>& data )
{
if (currentGraph)
{
if (splitEachArgument && data.size() > 1)
{
int16_t currentIndex = 0;
std::vector<double> splitData;
for (double d : data)
{
splitData = {d};
currentGraph->addData (subLabel + ofToString(currentIndex), splitData);
++currentIndex;
}
}
else
currentGraph->addData (subLabel, data);
}
}
void RapidVisualizer::update ( void )
{
if (currentGraph)
{
currentGraph->update();
}
}
void RapidVisualizer::drawMenu ( ofRectangle mainPosAndSize )
{
minimumGui.setPosition(graphState.positionAndSize.x, graphState.positionAndSize.y);
minimumGui.draw();
if (menuActive)
{
double expandedGuiXPos = graphState.positionAndSize.x + minimumGui.getWidth();
double expandedGuiYPos = graphState.positionAndSize.y;
double mainYPlusHeight = mainPosAndSize.y + mainPosAndSize.height;
double check = expandedGuiYPos + expandedGui.getHeight() - mainYPlusHeight;
if (check > 0)
{
expandedGuiYPos -= check;
}
expandedGui.setPosition(expandedGuiXPos, expandedGuiYPos);
expandedGui.draw();
}
}
void RapidVisualizer::draw ( void )
{
if (currentGraph)
{
currentGraph->draw();
}
//drawMenu();
menuActive = expandButton;
if (menuActive)
{
bool noneActive = true;
for (uint8_t i = 0; i < NUMVIEWTYPES; ++i)
{
if (viewTypes[i] && !oldTypeToggles[i])
{
std::fill(viewTypes, viewTypes + NUMVIEWTYPES, false);
viewTypes[i] = true;
setGraphType(static_cast<graphTypeT>(i));
}
if (viewTypes[i])
noneActive = false;
oldTypeToggles[i] = viewTypes[i];
}
if (noneActive && currentGraphType != NOT_INITIALIZED)
{
viewTypes[currentGraphType] = true;
}
if (splitEachArgument != splitArrayIntoGraphs)
{
// Dirty, solution for now
if (currentGraph)
currentGraph->reset();
splitEachArgument = splitArrayIntoGraphs;
}
}
}
uint32_t RapidVisualizer::getNumGraphs ( void ) const
{
if (currentGraph)
{
return currentGraph->getNumSubGraphs();
} else {
return 0;
}
}
#undef NUMVIEWTYPES
//
// RapidVisualizer.hpp
// RapidVisualizerOSC
//
// Created by James on 09/11/2017.
//
//
#ifndef RapidVisualizer_hpp
#define RapidVisualizer_hpp
#include <stdio.h>
#include <map>
#include "ofMain.h"
#include "Graph.hpp"
#include "BarChart.hpp"
#include "LineGraph.hpp"
#include "LineGraphHistory.hpp"
#include "ScopeWithHistory.hpp"
#define NUMVIEWTYPES 4
// TODO add historySize slider and init with default 256 or so
class RapidVisualizer {
public:
enum graphTypeT {
BAR_CHART,
LINE_GRAPH,
LINE_GRAPH_WITH_HISTORY,
SCOPE,
NOT_INITIALIZED
};
RapidVisualizer ( void );
~RapidVisualizer ( void );
void setup ( std::string label, graphTypeT graphType, Graph::graphLayout layout, bool splitEachArgument, ofRectangle positionAndSize );
void setup ( std::string label, graphTypeT graphType, uint32_t historySize, Graph::graphLayout layout, bool splitEachArgument, ofRectangle positionAndSize );
void setLabel ( std::string label );
void setGraphType ( graphTypeT graphType );
void setGraphType ( graphTypeT graphType, uint32_t historySize );
void setLayout ( Graph::graphLayout layout );
void setSplitEachArgument ( bool splitEachArgument );
void setPos ( ofVec2f pos );
void setSize ( ofVec2f size );
void setRect ( ofRectangle rect );
void setHistory ( uint32_t historySize );
void historySizeSliderChanged (int32_t &val);
void addData ( std::string subLabel, std::vector<double>& data );
void update ( void );
void drawMenu ( ofRectangle mainPosAndSize );
void draw ( void );
uint32_t getNumGraphs ( void ) const;
private:
graphTypeT currentGraphType = NOT_INITIALIZED;
Graph::GraphState graphState;
Graph* currentGraph = nullptr;
bool splitEachArgument = false;
bool menuActive = false;
ofxLabel graphName;
ofxLabel graphTypesLabel;
ofxLabel prefLabel;
ofxPanel minimumGui;
ofxToggle expandButton;
ofxPanel expandedGui;
ofxIntSlider historySizeSlider;
ofxToggle splitArrayIntoGraphs;
bool oldTypeToggles[NUMVIEWTYPES];
ofxToggle viewTypes[NUMVIEWTYPES];
};
#endif /* RapidVisualizer_hpp */
//
// RealTimeGraph.hpp
// RapidVisualizerOSC
//
// Created by James on 09/11/2017.
//
//
#ifndef RealTimeGraph_h
#define RealTimeGraph_h
#include <map>
#include <vector>
#include <string>
#include "Graph.hpp"
class RealTimeGraph : public Graph
{
public:
RealTimeGraph ( GraphState* state )
: Graph(state)
{
}
~RealTimeGraph ( void )
{
}
void addData ( std::string subLabel, std::vector<double>& data )
{
if ( subLabelData.find(subLabel) == subLabelData.end() ) {
// not found
subLabelData[subLabel] = DataContainer<std::vector<double>>();
}
subLabelData[subLabel].labelData = data;
subLabelData[subLabel].updateMinMax();
}
virtual void reset ( void )
{
subLabelData.clear();
}
virtual void updateRep ( void ) = 0; // update representation
virtual void drawSubGraph ( std::string subLabel, DataContainer<std::vector<double>>& data, ofRectangle subLabelrect ) = 0;
virtual void update ( void ) = 0;
void draw ( void )
{
uint32_t numElements = subLabelData.size();
uint16_t heightBetweenSubLabels = state->positionAndSize.height/numElements;
uint16_t subLabelY = 0;
ofSetColor (255,255,255);
ofDrawLine(state->positionAndSize.x, state->positionAndSize.y,
state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y);
std::map<std::string, DataContainer<std::vector<double>>>::iterator it;
for(it = subLabelData.begin(); it != subLabelData.end(); ++it)
{
std::string subLabel = it->first;
DataContainer<std::vector<double>>& data = it->second;
drawSubGraph (subLabel, data, ofRectangle(state->positionAndSize.x,
state->positionAndSize.y + subLabelY,
state->positionAndSize.width,
heightBetweenSubLabels));
// Draw label and background
drawTextLabel(subLabel, ofVec2f(state->positionAndSize.x + state->positionAndSize.width,
state->positionAndSize.y + subLabelY),
ofColor(100, 100, 100), ofColor(textColor), TextAlignment::RIGHT, false);
// Draw max value
drawTextLabel(ofToString(data.maxValue),
ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
state->positionAndSize.y + subLabelY),
ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, false);
// Increment Y position
subLabelY += heightBetweenSubLabels;
// Draw min value
// Could show actually found min value
drawTextLabel(ofToString((data.minValue < 0) ? -data.maxValue : 0),
ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
state->positionAndSize.y + subLabelY),
ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, true);
// Draw Line at bottom of graph
ofSetLineWidth(2.0);
ofSetColor (180,180,180);
ofDrawLine(state->positionAndSize.x, state->positionAndSize.y + subLabelY,
state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y + subLabelY);
}
}
uint32_t getNumSubGraphs ( void ) const
{
return subLabelData.size();
}
protected:
std::map <std::string, DataContainer<std::vector<double>>> subLabelData;
};
#endif /* RealTimeGraph_h */
//
// ScopeWithHistory.hpp
// RapidVisualizerOSC
//
// Created by James on 12/11/2017.
//
//
#ifndef ScopeWithHistory_h
#define ScopeWithHistory_h
#include <stdio.h>
#include "Graph.hpp"
class ScopeWithHistory : public Graph
{
public:
ScopeWithHistory ( GraphState* state ) : Graph (state)
{
//
}
~ScopeWithHistory ( void )
{
//
}
void addData ( std::string subLabel, std::vector<double>& data )
{
if (data.size() < state->historySize)
{
//FIXME: can be sped up by using the result of this search instead of accessing by[] again
if ( subLabelData.find(subLabel) == subLabelData.end() ) {
// not found
DataContainer<std::vector<double>> container;
container.labelData.resize(state->historySize);
std::fill(container.labelData.begin(), container.labelData.end(), 0.0);
subLabelData[subLabel] = container;
}
DataContainer<std::vector<double>>& dataRef = subLabelData[subLabel];
std::vector<double>& referencedList = dataRef.labelData;
while (referencedList.size() >= state->historySize)
{
referencedList.pop_back();
}
for (int32_t i = data.size()-1; i >= 0; --i)
{
double val = data[i];
if (val < dataRef.minValue)
dataRef.minValue = val;
if (val > dataRef.maxValue)
dataRef.maxValue = val;
if (fabs(val) > dataRef.maxValue)
dataRef.maxValue = fabs(val);
if (referencedList.size() + data.size() >= state->historySize)
{
referencedList[dataRef.iteratorPos] = val;
dataRef.iteratorPos = (dataRef.iteratorPos + data.size()) % state->historySize;
} else {
referencedList.insert(referencedList.begin(), val);
}
}
}
}
void updateRep ( void )
{
//
}
void drawSubGraph ( std::string subLabel, DataContainer<std::vector<double>>& data, ofRectangle subLabelRect )
{
double
minIn = 0,
minOut = 0,
maxOut = -subLabelRect.height,
startPosY = subLabelRect.height,
pointDistance = subLabelRect.width/data.labelData.size(),
separation = pointDistance/2;
//halfSeparation = separation/2;
bool drawZeroSep = false;
if (data.minValue < 0)
{ // Add negative part
startPosY = subLabelRect.height/2;
minIn = -data.maxValue;
minOut = subLabelRect.height/2;
maxOut /= 2;
drawZeroSep = true;
}
ofBeginShape();
ofFill();
ofVertex(subLabelRect.x, subLabelRect.y + startPosY);
double output = mapVals(data.labelData.front(), minIn, data.maxValue, minOut, maxOut );
ofVertex(subLabelRect.x, subLabelRect.y + startPosY + output);
uint32_t i = 0;
for (double d : data.labelData)
{
output = mapVals(d, minIn, data.maxValue, minOut, maxOut );
ofSetColor (graphColor);
ofVertex(subLabelRect.x + pointDistance * i + separation, subLabelRect.y + startPosY + output);
//ofDrawRectangle(subLabelRect.x + barSize * i + halfSeparation, subLabelRect.y + startPosY, barSize - separation, output );
++i;
}
output = mapVals(data.labelData.back(), minIn, data.maxValue, minOut, maxOut );
ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY + output);
ofVertex(subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
ofEndShape();
if (drawZeroSep)
{
ofSetLineWidth(1.25);
ofSetColor (175,150,150);
ofDrawLine(subLabelRect.x, subLabelRect.y + startPosY,
subLabelRect.x + subLabelRect.width, subLabelRect.y + startPosY);
}
}
void update ( void )
{
//
}
void reset ( void )
{
subLabelData.clear();
}
void draw ( void )
{
uint32_t numElements = subLabelData.size();
uint16_t heightBetweenSubLabels = state->positionAndSize.height/numElements;
uint16_t subLabelY = 0;
ofSetColor (255,255,255);
ofDrawLine(state->positionAndSize.x, state->positionAndSize.y,
state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y);
std::map<std::string, DataContainer<std::vector<double>>>::iterator it;
for(it = subLabelData.begin(); it != subLabelData.end(); ++it)
{
std::string subLabel = it->first;
DataContainer<std::vector<double>>& data = it->second;
drawSubGraph (subLabel, data, ofRectangle(state->positionAndSize.x,
state->positionAndSize.y + subLabelY,
state->positionAndSize.width,
heightBetweenSubLabels));
// Draw label and background
drawTextLabel(subLabel, ofVec2f(state->positionAndSize.x + state->positionAndSize.width,
state->positionAndSize.y + subLabelY),
ofColor(100, 100, 100), ofColor(textColor), TextAlignment::RIGHT, false);
// Draw max value
drawTextLabel(ofToString(data.maxValue),
ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
state->positionAndSize.y + subLabelY),
ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, false);
// Increment Y position
subLabelY += heightBetweenSubLabels;
// Draw min value
// Show actual min value?
drawTextLabel(ofToString((data.minValue < 0) ? -data.maxValue : 0),
ofVec2f(state->positionAndSize.x + state->positionAndSize.width/2,
state->positionAndSize.y + subLabelY),
ofColor(50, 50, 50), ofColor(255, 255, 255), TextAlignment::CENTER, true);
// Draw Line at bottom of graph
ofSetLineWidth(2.0);
ofSetColor (180,180,180);
ofDrawLine(state->positionAndSize.x, state->positionAndSize.y + subLabelY,
state->positionAndSize.x + state->positionAndSize.width, state->positionAndSize.y + subLabelY);
}
}
uint32_t getNumSubGraphs ( void ) const
{
return subLabelData.size();
}
protected:
std::map <std::string, DataContainer<std::vector<double>>> subLabelData;
};
#endif /* ScopeWithHistory_h */
//
// BITEnvironment.cpp
// Bitalino
//
// Created by James on 23/11/2017.
//
//
#include "BITEnvironment.hpp"
namespace BIT
{
void Environment::setup( std::string nameOfBitalino, ofRectangle posAndSize )
{
this->posAndSize = posAndSize;
float graphHeight = posAndSize.height / ( NUM_SYNTH_PARAMS + 1 );
bst.setup( nameOfBitalino );
ofRectangle bitGraphRect( posAndSize.x, posAndSize.y, posAndSize.width, graphHeight );
bitalinoGraph.setup( "Bitalino", ofColor( 100, 100, 255 ),
ofColor( 100, 255, 100 ), ofColor( 100, 100, 100 ), bitGraphRect, true, true, 2000 );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
ofRectangle graphRect( posAndSize.x, posAndSize.y + ( i + 1 ) * graphHeight, posAndSize.width, graphHeight );
graphs[ i ].setup( ParamNames[ i ], ofColor( 255, 255, 255 ),
ofColor( 255, 100, 100 ), ofColor( 100, 100, 100 ), graphRect, true, false, 2000 );
// Link buffers together
bst.loopDataBuffers[ i ].linkTo( &graphs[ i ].memory );
graphs[ i ].memory.linkTo( &bst.loopDataBuffers[ i ] );
}
// Only needs one way link
bst.bitalinoChannel.linkTo( &bitalinoGraph.memory );
}
void Environment::update ( void )
{ // This runs concurrent with ofx
// THIS can dissapear
RingBufferAny::VariableHeader headerInfo;
while ( bst.toEnvironment.anyAvailableForPop( headerInfo ) )
{
if ( headerInfo.type_index == typeid( RecordingSelection ) ) {
RecordingSelection sel;
bst.toEnvironment.pop( &sel, 1 );
if ( sel.position < 0 )
selectedTrainingParts.clear( );
else
selectedTrainingParts.push_back( sel.position );
} else if ( headerInfo.type_index == typeid( State ) ) {
selectedTrainingParts.clear( );
bst.toEnvironment.pop( &currentState, 1 );
switch ( currentState )
{
case PERFORMING:
bitalinoGraph.graphColor = ofColor( 100, 255, 100 );
bitalinoGraph.redraw( );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
graphs[ i ].graphColor = ofColor( 100, 100, 255 );
graphs[ i ].backgroundColor = ofColor( 255, 255, 255 );
graphs[ i ].redraw( );
}
break;
case RECORDING:
bitalinoGraph.graphColor = ofColor( 100, 255, 100 );
bitalinoGraph.redraw( );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
graphs[ i ].graphColor = ofColor( 255, 255, 255 );
graphs[ i ].backgroundColor = ofColor( 180, 100, 100 );
graphs[ i ].redraw( );
}
break;
case PLAYING:
bitalinoGraph.graphColor = ofColor( 100, 255, 100 );
bitalinoGraph.redraw( );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
graphs[ i ].graphColor = ofColor( 255, 100, 100 );
graphs[ i ].backgroundColor = ofColor( 100, 100, 100 );
graphs[ i ].redraw( );
}
break;
case TRAINING:
// Fallthrough style
case IDLE:
bitalinoGraph.graphColor = ofColor( 100, 100, 100 );
bitalinoGraph.redraw( );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
graphs[ i ].graphColor = ofColor( 100, 100, 100 );
graphs[ i ].backgroundColor = ofColor( 100, 100, 100 );
graphs[ i ].redraw( );
}
break;
default:
break;
}
} else if ( headerInfo.type_index == typeid( BITSequencerThread::RealtimeViewData ) ) {
BITSequencerThread::RealtimeViewData data[ headerInfo.valuesPassed ];
bst.toEnvironment.pop( data, headerInfo.valuesPassed );
for ( size_t i = 0; i < headerInfo.valuesPassed; ++i )
{
transport = data[ i ].transportPosition;
}
}
}
bitalinoGraph.update( );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
graphs[ i ].update( );
}
}
bool Environment::isMouseInside ( ofVec2f mousePos)
{
return posAndSize.inside( mousePos );
}
void Environment::mouseActive ( ofVec2f mousePos, int8_t mouseState, bool mouseStateChanged )
{
if ( currentState == PLAYING && selectedTrainingParts.empty( ) ) // Only then are buffers to be altered
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
PokeGraph& currentGraph = graphs[ i ];
if ( currentGraph.isMouseInside( mousePos ) )
{
if ( previousActive && previousActive != &currentGraph )
{
previousActive->mouseActive( mousePos, -1, true );
}
currentGraph.mouseActive( mousePos, mouseState, mouseStateChanged );
previousActive = &currentGraph;
}
}
}
void Environment::drawSelectedTrainingParts( void )
{
ofColor highlight( 255, 255, 255, 50 );
ofColor pillars( 255, 255, 255, 150 );
size_t size = selectedTrainingParts.size( );
int8_t complete = 0;
double previousPoint = 0;
for ( uint32_t i = 0; i < size; ++i )
{ // TODO fix wrap cases
double point = selectedTrainingParts[ i ];
complete = i % 2;
if ( i + 1 == size && complete == 0)
{
double transp = ( transport - point );
if ( transp < 0 )
{
selectedTrainingParts.insert( selectedTrainingParts.begin( ) + ( i + 1 ), 1.0 );
selectedTrainingParts.insert( selectedTrainingParts.begin( ) + ( i + 2 ), 0.0 );
i -= 1;
size += 2;
continue;
}
ofRectangle rect( posAndSize.x + point * posAndSize.width, posAndSize.y,
transp * posAndSize.width, posAndSize.height );
ofSetColor( highlight );
ofFill( );
ofDrawRectangle( rect );
ofSetColor( pillars );
ofSetLineWidth( 1.5f );
ofNoFill( );
ofDrawRectangle( rect );
ofSetColor( 255, 255, 255, 255 );
return;
}
if ( complete == 1 )
{
ofRectangle rect( posAndSize.x + previousPoint * posAndSize.width, posAndSize.y,
( point - previousPoint ) * posAndSize.width, posAndSize.height );
ofSetColor( highlight );
ofFill( );
ofDrawRectangle( rect );
ofSetColor( pillars );
ofSetLineWidth( 1.5f );
ofNoFill( );
ofDrawRectangle( rect );
}
previousPoint = point;
}
ofSetColor( 255, 255, 255, 255 );
}
void Environment::draw ( void )
{ // This runs concurrent with ofx
double xPosTransport = posAndSize.x + posAndSize.width * transport;
ofDrawLine( xPosTransport, posAndSize.y, xPosTransport, posAndSize.y + posAndSize.height );
bitalinoGraph.draw( );
if ( !selectedTrainingParts.empty( ) )
drawSelectedTrainingParts( );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
graphs[ i ].draw( );
}
bool connected = bst.rapidBitalino.bitalinoThread.isConnected( );
drawTextLabel( stateStrings[ static_cast< uint8_t >( currentState ) ], ofVec2f( posAndSize.x + 4, posAndSize.y + 21 ),
(( connected ) ? ofColor( 24, 219, 92 ) : ofColor( 255, 24, 24 ) ), ofColor( 10, 10, 10 ),
ofxBitmapText::TextAlignment::LEFT, false );
}
void Environment::resize ( ofRectangle posAndSize )
{
this->posAndSize = posAndSize;
float graphHeight = posAndSize.height / ( NUM_SYNTH_PARAMS + 1 );
bitalinoGraph.setRect( ofRectangle( posAndSize.x, posAndSize.y, posAndSize.width, graphHeight ) );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
graphs[ i ].setRect( ofRectangle( posAndSize.x, posAndSize.y + ( i + 1 ) * graphHeight, posAndSize.width, graphHeight ) );
}
}
void Environment::stop( void )
{
bst.stop();
}
void Environment::audioOut( float *output, int bufferSize, int nChannels )
{ // Audio thread sync callback
bst.audioOut(output, bufferSize, nChannels);
}
} // End namespace BIT::
//
// BITEnvironment.hpp
// Bitalino
//
// Created by James on 23/11/2017.
//
//
#ifndef BITEnvironment_hpp
#define BITEnvironment_hpp
#include <stdio.h>
#include <vector>
#include <atomic>
#include "ofMain.h"
#include "BITSequencerThread.hpp"
#include "PokeGraph.hpp"
namespace BIT
{
// ML Bitalino Synth environment
// Functions as the view + a portion of the controller ( PokeGraphs )
class Environment
{
public:
BITSequencerThread bst;
void setup ( std::string nameOfBitalino, ofRectangle posAndSize );
void update ( void );
bool isMouseInside ( ofVec2f mousePos );
void mouseActive ( ofVec2f mousePos, int8_t mouseState, bool mouseStateChanged );
void draw ( void );
void drawSelectedTrainingParts ( void );
void resize ( ofRectangle posAndSize );
void stop ( void );
void audioOut(float * output, int bufferSize, int nChannels);
private:
std::vector< double > selectedTrainingParts;
ofRectangle posAndSize;
PokeGraph* previousActive = nullptr;
PokeGraph bitalinoGraph; // To show the input in sync with output parameters
PokeGraph graphs[ NUM_SYNTH_PARAMS ]; // To show and allow editing of parameters
double transport;
State currentState;
};
} // End namespace BIT::
#endif /* BITSynth_hpp */
//
// BITSequencerThread.cpp
// Bitalino
//
// Created by James on 08/12/2017.
//
//
#include "BITSequencerThread.hpp"
namespace BIT
{
BITSequencerThread::BITSequencerThread ( void ) : ThreadedProcess ()
{
setState(IDLE);
recording.store(false);
selectingData.store(false);
transport.store(0.0);
}
BITSequencerThread::~BITSequencerThread ( void )
{
}
void BITSequencerThread::setup( std::string nameOfBitalino )
{
// Set callback for when model is done training (called in rapidMixThread.update method)
rapidMixThread.doneTrainingCallback = [&] ( void )
{
//printf("New Model Trained\n");
sendRunDataToOFX();
trainingDone = true;
transport.store(0.0);
setState( PERFORMING );
//trainingText = "New Model Trained";
};
// Set buttonPressedCallback for rapidBitalino
rapidBitalino.buttonPressedCallback = std::bind(&BITSequencerThread::buttonPressed, this);
// Set buttonReasedCallback for rapidBitalino
rapidBitalino.buttonReleasedCallback = std::bind(&BITSequencerThread::buttonReleased, this);
// Set up training thread
rapidMixThread.setup();
// Set up BITalino accessor + thread
rapidBitalino.setup( nameOfBitalino, 10 ); // downsampling 10 of the data (TODO experiment with different settings with bayes)
toEnvironment.setup( 8192 ); // Leave room for 16kb of memory to be used in the ring-queue
bitalinoChannel.setup( 2000 );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
loopDataBuffers[ i ].setup( 2000 );
}
State s = currentState.load( );
toEnvironment.push( &s, 1 ); // Send initial state
// Start the thread
startThread( 10000 ); // Sleep 10 ms
}
void BITSequencerThread::setState ( State state )
{
toEnvironment.push ( &state, 1 );
this->currentState.store(state);
}
State BITSequencerThread::getState ( void ) const
{
return this->currentState.load();
}
const char* BITSequencerThread::getStateString ( void )
{
return stateStrings[static_cast<uint8_t>(getState())];
}
void BITSequencerThread::stop( void )
{
// Stop all asynchronous threads
this->stopThread();
rapidMixThread.stopThread();
rapidBitalino.stop();
}
void BITSequencerThread::audioOut( float *output, int bufferSize, int nChannels )
{ // Audio thread sync callback
synthesizer.audioOut(output, bufferSize, nChannels);
}
double BITSequencerThread::getTransport ( void )
{
return transport.load( );
}
void BITSequencerThread::buttonPressed ( void )
{ // Bitalino button press
longSelectionTimer.reset( );
switch ( currentState.load( ) )
{ // Run update for state
case IDLE:
//printf( "Started \n" );
generateFirstData( );
setState( PLAYING );
break;
case PERFORMING:
{
if ( trainingDone )
{
if ( recording.load( ) )
{
//printf( "Playing recorded gestures \n" );
recording.store( false );
setState( PLAYING );
} else {
//printf( "Started recording \n" );
recording.store( true );
transport.store( 0.0 );
// TODO: create extra state for just recording?
State recState = RECORDING; // Only used by environment for color change
toEnvironment.push( &recState, 1 );
}
}
break;
}
case PLAYING:
{
if ( !selectingData.load( ) )
{
RecordingSelection sel{ static_cast< double >( loopPosition ) / loopSize };
toEnvironment.push( &sel, 1 );
//printf( "Selecting data for example \n" );
trainingData.createNewPhrase( "NewPhrase" );
//printf( "Added training element, now size of %lu\n", trainingData.trainingSet.size( ) );
selectingData.store( true );
}
break;
}
default:
break;
}
}
void BITSequencerThread::buttonReleased( void )
{ // Bitalino button release
State s = currentState.load();
switch ( s )
{ // Run update for state
case PLAYING:
{
if (selectingData.load())
{
if (longSelectionTimer.getTimeElapsed() > longPress)
{
RecordingSelection sel{ static_cast< double >( loopPosition ) / loopSize };
toEnvironment.push( &sel, 1 );
//printf("Recorded example \n");
selectingData.store(false);
} else {
//printf("Throwing away last training sample, press was for stopping \n");
trainingData.trainingSet.pop_back();
RecordingSelection sel{ -1.0 };
toEnvironment.push( &sel, 1 );
bitalinoChannel.setRangeToConstant( 0, loopSize, 0.0 );
// Draw line here for next part ?
if (trainingData.trainingSet.size() >= 1)
{
//printf("Training on selected data \n");
RecordingSelection sel{ -1.0 };
toEnvironment.push( &sel, 1 );
trainingDone = false;
rapidMixThread.train(trainingData);
setState( TRAINING );
} else {
//printf("Reinitializing");
generateFirstData();
toEnvironment.push(&s, 1); // Make sure everything gets reinitialized
}
selectingData.store(false);
}
}
break;
}
default:
break;
}
}
void BITSequencerThread::mainThreadCallback( void )
{ // Process thread sync callback
// Get input data (BITalino):
RealtimeViewData viewData;
std::vector< double > inputData = rapidBitalino.update( ); // Update method uses ringbuffer and is threadsafe
State curState = currentState.load( );
bool newState = ( curState != previousState );
if ( newState )
{
dataPlayTimer.reset( );
longSelectionTimer.reset( );
switch ( curState )
{ // Run inits for states
case PERFORMING:
loop.clear( );
loopSize = 0;
break;
case PLAYING:
loopPosition = 0;
trainingData.trainingSet.clear( );
break;
default:
break;
}
} else
switch ( curState )
{ // Run update for state
case PERFORMING:
{ // Update case for performing state
if (dataPlayTimer.getTimeElapsed( ) > controlRate && trainingDone)
{
if ( !recording.load( ) )
transport.store( inputData[0] / maxValueEMG );
SynthControlData ctrlData( rapidMixThread.model->run( inputData ) );
synthesizer.controlDataBuffer.push( &ctrlData, 1 );
currentControlData = ctrlData;
if ( recording.load( ) )
{
bitalinoChannel.resizeBounded( loopSize + 1 );
bitalinoChannel[ loopSize ] = inputData[ 0 ];
for ( uint32_t p = 0; p < NUM_SYNTH_PARAMS; ++p )
{
loopDataBuffers[ p ].resizeBounded( loopSize + 1 );
loopDataBuffers[ p ][ loopSize ] = ctrlData.values[ p ];
}
++loopSize;
}
dataPlayTimer.reset( );
}
break;
}
case PLAYING:
{ // Update case for playing state
if (dataPlayTimer.getTimeElapsed() > controlRate)
{
bitalinoChannel[ loopPosition ] = inputData[ 0 ];
SynthControlData ctrlData;
for ( uint32_t p = 0; p < NUM_SYNTH_PARAMS; ++p )
{
ctrlData.values[ p ] = loopDataBuffers[ p ][ loopPosition ];
}
synthesizer.controlDataBuffer.push(&ctrlData, 1);
currentControlData = ctrlData;
if (++loopPosition >= loopSize)
loopPosition = 0;
transport.store( static_cast< double >( loopPosition ) / loopSize );
if (selectingData.load())
{ // Add element to trainingData
std::vector<double> outputTraining = ctrlData.getValuesAsVector( );
trainingData.addElement(inputData, outputTraining);
}
dataPlayTimer.reset();
}
break;
}
default:
break;
}
if ( currentState.load( ) != IDLE )
{
viewData.transportPosition = transport.load( );
toEnvironment.push( &viewData, 1 );
}
bitalinoChannel.update( );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
loopDataBuffers[ i ].update( );
}
rapidMixThread.update();
previousState = curState;
}
void BITSequencerThread::generateFirstData ( void )
{
uint8_t midiStart = 55;
uint32_t doremiSize = 2;
// Calculate new size of buffers
uint32_t resizeSize = doremiSize * majorScaleSize * samplesPerExample;
// Resize all buffers
bitalinoChannel.resizeBounded( resizeSize );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
loopDataBuffers[ i ].resizeBounded( resizeSize );
}
// Calculate doremi ladder and also random data blocks for other parameters
int index = 0;
for (uint32_t i = 0; i < doremiSize; ++i)
{
for (uint32_t step = 0; step < majorScaleSize; ++step)
{
double frequency = MTOF( midiStart + ( i * 12 ) + majorScale[ step ] );
SynthControlData ctrlData;
ctrlData.values[ CARRIER ] = frequency;
for ( uint16_t x = 0; x < NUM_SYNTH_PARAMS; ++x )
{
if ( x != static_cast< uint8_t >( CARRIER ) )
ctrlData.values[ x ] = ( ( double ) rand( ) / RAND_MAX );
}
for ( uint32_t p = 0; p < NUM_SYNTH_PARAMS; ++p )
{
loopDataBuffers[ p ].setRangeToConstant( index, index + samplesPerExample, ctrlData.values[ p ] );
}
index += samplesPerExample;
}
}
bitalinoChannel.setRangeToConstant( 0, resizeSize, 0 );
loopPosition = 0;
loopSize = resizeSize;
}
void BITSequencerThread::generateFirstRandomData ( void )
{ // NO LONGER USED
for (uint32_t i = 0; i < 10; ++i)
{
SynthControlData ctrlData;
for (uint16_t x = 0; x < NUM_SYNTH_PARAMS; ++x)
{
ctrlData.values[x] = ((double)rand()/RAND_MAX);
}
for (uint32_t y = 0; y < samplesPerExample; ++y)
loop.push_back(ctrlData);
}
}
void BITSequencerThread::sendRunDataToOFX ( void )
{ // Sweeps through the trained model and shows the input vs the outputs
uint32_t numValues = 256;
bitalinoChannel.resizeBounded( numValues );
for ( uint32_t i = 0; i < NUM_SYNTH_PARAMS; ++i )
{
loopDataBuffers[ i ].resizeBounded( numValues );
}
for ( int i = 0.0; i < numValues; i += 1 )
{
double inp = static_cast< double >( i * 2 );
std::vector< double > input( NUM_INPUTS_FOR_TRAINING, inp );
bitalinoChannel[ i ] = inp;
std::vector< double > outputs = rapidMixThread.model->run( input );
for ( uint32_t p = 0; p < NUM_SYNTH_PARAMS; ++p )
{
loopDataBuffers[ p ][ i ] = outputs[ p ];
}
}
}
}
//
// BITSequencerThread.hpp
// Bitalino
//
// Created by James on 08/12/2017.
//
//
#ifndef BITSequencerThread_hpp
#define BITSequencerThread_hpp
#include <stdio.h>
#include <functional>
#include "Timer.h"
#include "SigTools.hpp"
#include "RapidBitalino.h"
#include "RapidMixThread.h"
#include "BITSynth.hpp"
#include "ThreadedProcess.h"
#include "RingBufferAny.hpp"
#include "LinkedBuffer.hpp"
namespace BIT
{
class BITSequencerThread : public ThreadedProcess
{
// Functions as the model
public:
LinkedBuffer< double > bitalinoChannel;
LinkedBuffer< double > loopDataBuffers[ NUM_SYNTH_PARAMS ];
// Todo move back to protected/private (public for testing w/o bitalino)
void buttonPressed ( void );
void buttonReleased ( void );
// BITalino thread, TODO: threadsafe accessors etc
RapidBitalino rapidBitalino;
// Synth which can only be changed by ringbuffer (threadsafe)
Synth synthesizer;
// Threadsafe queue of events
RingBufferAny toEnvironment;
BITSequencerThread ( void );
~BITSequencerThread ( void );
void setup ( std::string nameOfBitalino );
void setState ( State state ); // Atomic set
State getState ( void ) const; // Atomic get
const char* getStateString ( void );
void stop ( void );
void audioOut(float * output, int bufferSize, int nChannels);
double getTransport ( void );
struct RealtimeViewData {
double transportPosition = 0.0;
};
protected:
//void buttonPressed ( void );
//void buttonReleased ( void );
void mainThreadCallback ( void );
void generateFirstData ( void );
void generateFirstRandomData ( void );
void sendRunDataToOFX ( void );
private:
// Rapidmix stuff
RapidMixThread rapidMixThread;
rapidmix::trainingData trainingData;
bool gatheringTrainingData = false;
bool trainingDone = false;
// Other stuff
uint32_t sampleRate = 44100;
uint32_t controlRate = 15; // 15ms
uint32_t samplesPerExample = 50;
// DEBUGDEBUG
//--
// State
std::atomic< State > currentState;
State previousState; // (synced with Data thread)
std::atomic< bool > recording;
std::atomic< bool > selectingData;
// Data thread synced
BIT::SynthControlData currentControlData;
std::vector< BIT::SynthControlData > loop;
int loopSize;
int loopPosition;
// Timers
Timer dataPlayTimer;
Timer longSelectionTimer;
uint32_t longPress = 400; // 400ms
double maxValueEMG = 512.0;
std::atomic< double > transport;
};
}
#endif /* BITSequencerThread_hpp */