@@ -71,6 +71,7 @@ else | |||
$(MAKE) -C tal-reverb-2/LV2 | |||
$(MAKE) -C tal-reverb-3/LV2 | |||
$(MAKE) -C tal-vocoder-2/LV2 | |||
$(MAKE) -C temper/LV2 | |||
$(MAKE) -C vex/LV2 | |||
$(MAKE) -C wolpertinger/LV2 | |||
endif | |||
@@ -111,6 +112,7 @@ vst: libs | |||
$(MAKE) -C tal-reverb-2/VST | |||
$(MAKE) -C tal-reverb-3/VST | |||
$(MAKE) -C tal-vocoder-2/VST | |||
$(MAKE) -C temper/VST | |||
$(MAKE) -C vex/VST | |||
$(MAKE) -C wolpertinger/VST | |||
@@ -150,6 +152,7 @@ clean: | |||
$(MAKE) clean -C tal-reverb-2/LV2 | |||
$(MAKE) clean -C tal-reverb-3/LV2 | |||
$(MAKE) clean -C tal-vocoder-2/LV2 | |||
$(MAKE) clean -C temper/LV2 | |||
$(MAKE) clean -C vex/LV2 | |||
$(MAKE) clean -C wolpertinger/LV2 | |||
@@ -185,6 +188,7 @@ clean: | |||
$(MAKE) clean -C tal-reverb-2/VST | |||
$(MAKE) clean -C tal-reverb-3/VST | |||
$(MAKE) clean -C tal-vocoder-2/VST | |||
$(MAKE) clean -C temper/VST | |||
$(MAKE) clean -C vex/VST | |||
$(MAKE) clean -C wolpertinger/VST | |||
@@ -0,0 +1,19 @@ | |||
dofile("../../../scripts/make-project.lua") | |||
package = make_juce_lv2_project("Temper") | |||
if (windows) then | |||
-- TODO | |||
elseif (macosx) then | |||
-- TODO | |||
else | |||
package.linkoptions = { package.linkoptions, "`pkg-config --libs gl`" } | |||
end | |||
package.files = { | |||
matchfiles ( | |||
"../source/*.cpp", | |||
"../../../libs/juce-plugin/JucePluginMain.cpp" | |||
) | |||
} |
@@ -0,0 +1,19 @@ | |||
dofile("../../../scripts/make-project.lua") | |||
package = make_juce_vst_project("Temper") | |||
if (windows) then | |||
-- TODO | |||
elseif (macosx) then | |||
-- TODO | |||
else | |||
package.linkoptions = { package.linkoptions, "`pkg-config --libs gl`" } | |||
end | |||
package.files = { | |||
matchfiles ( | |||
"../source/*.cpp", | |||
"../../../libs/juce-plugin/JucePluginMain.cpp" | |||
) | |||
} |
@@ -0,0 +1,44 @@ | |||
/* ========================================================================================= | |||
This is an auto-generated file: Any edits you make may be overwritten! | |||
*/ | |||
#pragma once | |||
namespace BinaryData | |||
{ | |||
extern const char* Background_png; | |||
const int Background_pngSize = 1105568; | |||
extern const char* BeeStingPreset_xml; | |||
const int BeeStingPreset_xmlSize = 250; | |||
extern const char* DefaultPreset_xml; | |||
const int DefaultPreset_xmlSize = 249; | |||
extern const char* FlyingUnitedPreset_xml; | |||
const int FlyingUnitedPreset_xmlSize = 252; | |||
extern const char* GraphBackground_png; | |||
const int GraphBackground_pngSize = 179814; | |||
extern const char* MontserratLight_otf; | |||
const int MontserratLight_otfSize = 91496; | |||
extern const char* MorningAtTheDMVPreset_xml; | |||
const int MorningAtTheDMVPreset_xmlSize = 254; | |||
extern const char* StubbedToePreset_xml; | |||
const int StubbedToePreset_xmlSize = 252; | |||
// Points to the start of a list of resource names. | |||
extern const char* namedResourceList[]; | |||
// Number of elements in the namedResourceList array. | |||
const int namedResourceListSize = 8; | |||
// If you provide the name of one of the binary resource variables above, this function will | |||
// return the corresponding data and its size (or a null pointer if the name isn't found). | |||
const char* getNamedResource (const char* resourceNameUTF8, int& dataSizeInBytes) throw(); | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
============================================================================== | |||
FaustUIBridge.cpp | |||
Created: 9 Feb 2017 5:59:55pm | |||
Author: Nick Thompson | |||
============================================================================== | |||
*/ | |||
#include "FaustUIBridge.h" | |||
FaustUIBridge::FaustUIBridge(AudioProcessorValueTreeState& vts) | |||
: valueTreeState(vts) | |||
{ | |||
} | |||
FaustUIBridge::~FaustUIBridge() | |||
{ | |||
for (int i = 0; i < listenerAssignments.size(); ++i) | |||
{ | |||
ParameterListenerPair p = listenerAssignments.getUnchecked(i); | |||
String paramId = p.paramId; | |||
FaustUIBridgeListener* listener = p.listener; | |||
valueTreeState.removeParameterListener(paramId, listener); | |||
} | |||
listenerAssignments.clear(); | |||
} | |||
void FaustUIBridge::addHorizontalSlider(const char *label, float *zone, float init, float min, float max, float step) | |||
{ | |||
// Create the AudioProcessor parameter if not exists | |||
if (!valueTreeState.getParameter(label)) | |||
valueTreeState.createAndAddParameter(label, label, String(), | |||
NormalisableRange<float>(min, max), init, nullptr, nullptr); | |||
// Attach the listener to keep the internal dsp values up to date | |||
FaustUIBridgeListener* l = new FaustUIBridgeListener(zone); | |||
listeners.add(l); | |||
listenerAssignments.add(ParameterListenerPair(String(label), l)); | |||
valueTreeState.addParameterListener(label, l); | |||
} |
@@ -0,0 +1,132 @@ | |||
/* | |||
============================================================================== | |||
FaustUIBridge.h | |||
Created: 9 Feb 2017 5:59:55pm | |||
Author: Nick Thompson | |||
============================================================================== | |||
*/ | |||
#ifndef FAUSTUIBRIDGE_H_INCLUDED | |||
#define FAUSTUIBRIDGE_H_INCLUDED | |||
#include "JuceHeader.h" | |||
#include "faust/gui/UI.h" | |||
//============================================================================== | |||
/** | |||
This class interfaces with the Faust UI paradigm, mapping control parameters | |||
into AudioProcessorParameters on a provided AudioProcessorValueTreeState. | |||
*/ | |||
class FaustUIBridge : public UI | |||
{ | |||
public: | |||
/** Construct a FaustUIBridge instance with the associated AudioProcessorValueTreeState. */ | |||
FaustUIBridge(AudioProcessorValueTreeState& vts); | |||
/** Destructor. */ | |||
virtual ~FaustUIBridge() override; | |||
//============================================================================== | |||
/** Widget Layout | |||
The following methods implement the widget layout functions. | |||
Because we intend for an AudioProcessorEditor to manage the | |||
visual aspect of the plugin, these methods are no-ops. | |||
*/ | |||
virtual void openTabBox(const char* label) override {}; | |||
virtual void openHorizontalBox(const char* label) override {}; | |||
virtual void openVerticalBox(const char* label) override {}; | |||
virtual void closeBox() override {}; | |||
//============================================================================== | |||
/** Active Widgets | |||
The following methods implement the active widget functions | |||
by constructing an AudioProcessorParameter and installing a | |||
FaustUIBridgeListener to propagate changes back to the corresponding | |||
zone. | |||
*/ | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) override {}; | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) override {}; | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override {}; | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override; | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override {}; | |||
//============================================================================== | |||
/** Passive Widgets | |||
The following methods implement the passive widget functions. | |||
Again, because we intend for an AudioProcessorEditor to manage the | |||
visual aspect of the plugin, these methods are no-ops. | |||
*/ | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override {}; | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override {}; | |||
//============================================================================== | |||
/** Metadata declarations | |||
An additional no-op to implement the last piece of the Faust UI interface. | |||
*/ | |||
virtual void declare(FAUSTFLOAT*, const char*, const char*) override {}; | |||
//============================================================================== | |||
/** This class implements a simple callback which reacts to a value change on a | |||
given AudioProcessorParameter. | |||
We construct an instance of this class for each Faust zone declared via the | |||
active widget methods above. | |||
*/ | |||
class FaustUIBridgeListener : public AudioProcessorValueTreeState::Listener | |||
{ | |||
public: | |||
FaustUIBridgeListener(float* zone) : m_zone(zone) {}; | |||
virtual ~FaustUIBridgeListener() override {}; | |||
virtual void parameterChanged (const String& parameterID, float newValue) override | |||
{ | |||
*m_zone = newValue; | |||
}; | |||
private: | |||
float* m_zone; | |||
}; | |||
private: | |||
//============================================================================== | |||
/** | |||
This struct associates an AudioProcessorParameter id with a FaustUIBridgeListener | |||
object which as been attached to the valueTreeState. | |||
*/ | |||
struct ParameterListenerPair | |||
{ | |||
//============================================================================== | |||
/** Constructor. | |||
@param paramId The String Id of the AudioProcessorParameter. | |||
@param listener The attached FaustUIBridgeListener. | |||
*/ | |||
ParameterListenerPair(String paramId, FaustUIBridgeListener* listener) | |||
: paramId(paramId), listener(listener) {}; | |||
String paramId; | |||
FaustUIBridgeListener* listener; | |||
}; | |||
// A reference to the AudioProcessor's value tree so that we can map the faust | |||
// UI parameters to AudioProcessor parameters. | |||
AudioProcessorValueTreeState& valueTreeState; | |||
// Maintain an array associating AudioProcessorParameters to the Listeners that have been | |||
// installed on those parameters. | |||
Array<ParameterListenerPair> listenerAssignments; | |||
// And an array of listeners to ensure the mapping between internal value tree values | |||
// match the float* zone members of the faust implementation. | |||
OwnedArray<AudioProcessorValueTreeState::Listener> listeners; | |||
}; | |||
#endif // FAUSTUIBRIDGE_H_INCLUDED |
@@ -0,0 +1,34 @@ | |||
/* | |||
IMPORTANT! This file is auto-generated each time you save your | |||
project - if you alter its contents, your changes may be overwritten! | |||
This is the header file that your files should include in order to get all the | |||
JUCE library headers. You should avoid including the JUCE headers directly in | |||
your own source files, because that wouldn't pick up the correct configuration | |||
options for your app. | |||
*/ | |||
#ifndef __APPHEADERFILE_JH7QDM__ | |||
#define __APPHEADERFILE_JH7QDM__ | |||
#include "JucePluginMain.h" | |||
#include "BinaryData.h" | |||
#if ! DONT_SET_USING_JUCE_NAMESPACE | |||
// If your code uses a lot of JUCE classes, then this will obviously save you | |||
// a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE. | |||
using namespace juce; | |||
#endif | |||
#if ! JUCE_DONT_DECLARE_PROJECTINFO | |||
namespace ProjectInfo | |||
{ | |||
const char* const projectName = "Temper"; | |||
const char* const versionString = "1.0.3"; | |||
const int versionNumber = 0x10003; | |||
} | |||
#endif | |||
#endif // __APPHEADERFILE_JH7QDM__ |
@@ -0,0 +1,139 @@ | |||
/* | |||
IMPORTANT! This file is auto-generated each time you save your | |||
project - if you alter its contents, your changes may be overwritten! | |||
There's a section below where you can add your own custom code safely, and the | |||
Projucer will preserve the contents of that block, but the best way to change | |||
any of these definitions is by using the Projucer's project settings. | |||
Any commented-out settings will assume their default values. | |||
*/ | |||
#pragma once | |||
//============================================================================== | |||
#ifndef JucePlugin_Enable_IAA | |||
#define JucePlugin_Enable_IAA 0 | |||
#endif | |||
#ifndef JucePlugin_Name | |||
#define JucePlugin_Name "Temper" | |||
#endif | |||
#ifndef JucePlugin_Desc | |||
#define JucePlugin_Desc "Modern digital distortion." | |||
#endif | |||
#ifndef JucePlugin_Manufacturer | |||
#define JucePlugin_Manufacturer "Creative Intent" | |||
#endif | |||
#ifndef JucePlugin_ManufacturerWebsite | |||
#define JucePlugin_ManufacturerWebsite "http://www.creativeintent.co" | |||
#endif | |||
#ifndef JucePlugin_ManufacturerEmail | |||
#define JucePlugin_ManufacturerEmail "" | |||
#endif | |||
#ifndef JucePlugin_ManufacturerCode | |||
#define JucePlugin_ManufacturerCode 0x4376696e // 'Cvin' | |||
#endif | |||
#ifndef JucePlugin_PluginCode | |||
#define JucePlugin_PluginCode 0x546d7072 // 'Tmpr' | |||
#endif | |||
#ifndef JucePlugin_IsSynth | |||
#define JucePlugin_IsSynth 0 | |||
#endif | |||
#ifndef JucePlugin_WantsMidiInput | |||
#define JucePlugin_WantsMidiInput 0 | |||
#endif | |||
#ifndef JucePlugin_ProducesMidiOutput | |||
#define JucePlugin_ProducesMidiOutput 0 | |||
#endif | |||
#ifndef JucePlugin_IsMidiEffect | |||
#define JucePlugin_IsMidiEffect 0 | |||
#endif | |||
#ifndef JucePlugin_EditorRequiresKeyboardFocus | |||
#define JucePlugin_EditorRequiresKeyboardFocus 0 | |||
#endif | |||
#ifndef JucePlugin_Version | |||
#define JucePlugin_Version 1.0.3 | |||
#endif | |||
#ifndef JucePlugin_VersionCode | |||
#define JucePlugin_VersionCode 0x10003 | |||
#endif | |||
#ifndef JucePlugin_VersionString | |||
#define JucePlugin_VersionString "1.0.3" | |||
#endif | |||
#ifndef JucePlugin_VSTUniqueID | |||
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode | |||
#endif | |||
#ifndef JucePlugin_VSTCategory | |||
#define JucePlugin_VSTCategory kPlugCategEffect | |||
#endif | |||
#ifndef JucePlugin_AUMainType | |||
#define JucePlugin_AUMainType kAudioUnitType_Effect | |||
#endif | |||
#ifndef JucePlugin_AUSubType | |||
#define JucePlugin_AUSubType JucePlugin_PluginCode | |||
#endif | |||
#ifndef JucePlugin_AUExportPrefix | |||
#define JucePlugin_AUExportPrefix TemperAU | |||
#endif | |||
#ifndef JucePlugin_AUExportPrefixQuoted | |||
#define JucePlugin_AUExportPrefixQuoted "TemperAU" | |||
#endif | |||
#ifndef JucePlugin_AUManufacturerCode | |||
#define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode | |||
#endif | |||
#ifndef JucePlugin_CFBundleIdentifier | |||
#define JucePlugin_CFBundleIdentifier com.creativeintent.temper | |||
#endif | |||
#ifndef JucePlugin_RTASCategory | |||
#define JucePlugin_RTASCategory ePlugInCategory_None | |||
#endif | |||
#ifndef JucePlugin_RTASManufacturerCode | |||
#define JucePlugin_RTASManufacturerCode JucePlugin_ManufacturerCode | |||
#endif | |||
#ifndef JucePlugin_RTASProductId | |||
#define JucePlugin_RTASProductId JucePlugin_PluginCode | |||
#endif | |||
#ifndef JucePlugin_RTASDisableBypass | |||
#define JucePlugin_RTASDisableBypass 0 | |||
#endif | |||
#ifndef JucePlugin_RTASDisableMultiMono | |||
#define JucePlugin_RTASDisableMultiMono 0 | |||
#endif | |||
#ifndef JucePlugin_AAXIdentifier | |||
#define JucePlugin_AAXIdentifier com.creativeintent.temper | |||
#endif | |||
#ifndef JucePlugin_AAXManufacturerCode | |||
#define JucePlugin_AAXManufacturerCode JucePlugin_ManufacturerCode | |||
#endif | |||
#ifndef JucePlugin_AAXProductId | |||
#define JucePlugin_AAXProductId JucePlugin_PluginCode | |||
#endif | |||
#ifndef JucePlugin_AAXCategory | |||
#define JucePlugin_AAXCategory AAX_ePlugInCategory_Dynamics | |||
#endif | |||
#ifndef JucePlugin_AAXDisableBypass | |||
#define JucePlugin_AAXDisableBypass 0 | |||
#endif | |||
#ifndef JucePlugin_AAXDisableMultiMono | |||
#define JucePlugin_AAXDisableMultiMono 0 | |||
#endif | |||
#ifndef JucePlugin_IAAType | |||
#define JucePlugin_IAAType 0x61757278 // 'aurx' | |||
#endif | |||
#ifndef JucePlugin_IAASubType | |||
#define JucePlugin_IAASubType JucePlugin_PluginCode | |||
#endif | |||
#ifndef JucePlugin_IAAName | |||
#define JucePlugin_IAAName "Creative Intent: Temper" | |||
#endif | |||
#define JucePlugin_LV2URI "https://github.com/creativeintent/temper" | |||
#define JucePlugin_LV2Category "DistortionPlugin" | |||
#define JucePlugin_WantsLV2Latency 0 | |||
#define JucePlugin_WantsLV2State 0 | |||
#define JucePlugin_WantsLV2StateString 0 | |||
#define JucePlugin_WantsLV2Presets 1 | |||
#define JucePlugin_WantsLV2TimePos 0 |
@@ -0,0 +1,337 @@ | |||
/* | |||
============================================================================== | |||
This is an automatically generated GUI class created by the Projucer! | |||
Be careful when adding custom code to these files, as only the code within | |||
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded | |||
and re-saved. | |||
Created with Projucer version: 4.3.0 | |||
------------------------------------------------------------------------------ | |||
The Projucer is part of the JUCE library - "Jules' Utility Class Extensions" | |||
Copyright (c) 2015 - ROLI Ltd. | |||
============================================================================== | |||
*/ | |||
//[Headers] You can add your own extra header files here... | |||
//[/Headers] | |||
#include "MainComponent.h" | |||
//[MiscUserDefs] You can add your own user definitions and misc code here... | |||
typedef AudioProcessorValueTreeState::SliderAttachment SliderAttachment; | |||
//[/MiscUserDefs] | |||
//============================================================================== | |||
MainComponent::MainComponent (AudioProcessorValueTreeState& vts) | |||
: m_vts(vts) | |||
{ | |||
//[Constructor_pre] You can add your own custom stuff here.. | |||
//[/Constructor_pre] | |||
addAndMakeVisible (m_cutoffSlider = new Slider ("Cutoff")); | |||
m_cutoffSlider->setRange (100, 20000, 0); | |||
m_cutoffSlider->setSliderStyle (Slider::RotaryHorizontalVerticalDrag); | |||
m_cutoffSlider->setTextBoxStyle (Slider::NoTextBox, true, 80, 20); | |||
addAndMakeVisible (m_resoSlider = new Slider ("Resonance")); | |||
m_resoSlider->setRange (1, 8, 0); | |||
m_resoSlider->setSliderStyle (Slider::RotaryHorizontalVerticalDrag); | |||
m_resoSlider->setTextBoxStyle (Slider::NoTextBox, true, 80, 20); | |||
addAndMakeVisible (m_driveSlider = new Slider ("Drive")); | |||
m_driveSlider->setRange (-10, 10, 0); | |||
m_driveSlider->setSliderStyle (Slider::RotaryHorizontalVerticalDrag); | |||
m_driveSlider->setTextBoxStyle (Slider::NoTextBox, true, 80, 20); | |||
addAndMakeVisible (m_curveSlider = new Slider ("Curve")); | |||
m_curveSlider->setRange (0.1, 4, 0); | |||
m_curveSlider->setSliderStyle (Slider::RotaryHorizontalVerticalDrag); | |||
m_curveSlider->setTextBoxStyle (Slider::NoTextBox, true, 80, 20); | |||
addAndMakeVisible (m_satSlider = new Slider ("Saturation")); | |||
m_satSlider->setRange (0, 1, 0); | |||
m_satSlider->setSliderStyle (Slider::RotaryHorizontalVerticalDrag); | |||
m_satSlider->setTextBoxStyle (Slider::NoTextBox, true, 80, 20); | |||
addAndMakeVisible (m_feedbackSlider = new Slider ("Feedback")); | |||
m_feedbackSlider->setRange (-60, -24, 0); | |||
m_feedbackSlider->setSliderStyle (Slider::RotaryHorizontalVerticalDrag); | |||
m_feedbackSlider->setTextBoxStyle (Slider::NoTextBox, true, 80, 20); | |||
addAndMakeVisible (m_gainSlider = new Slider ("Level")); | |||
m_gainSlider->setRange (-24, 24, 0); | |||
m_gainSlider->setSliderStyle (Slider::RotaryHorizontalVerticalDrag); | |||
m_gainSlider->setTextBoxStyle (Slider::NoTextBox, true, 80, 20); | |||
addAndMakeVisible (m_cutoffLabel = new Label ("Cutoff Label", | |||
TRANS("CUTOFF"))); | |||
m_cutoffLabel->setFont (Font (15.00f, Font::plain)); | |||
m_cutoffLabel->setJustificationType (Justification::centred); | |||
m_cutoffLabel->setEditable (false, false, false); | |||
m_cutoffLabel->setColour (Label::textColourId, Colour (0xffff8917)); | |||
m_cutoffLabel->setColour (TextEditor::textColourId, Colours::black); | |||
m_cutoffLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000)); | |||
addAndMakeVisible (m_resoLabel = new Label ("Resonance Label", | |||
TRANS("RESONANCE"))); | |||
m_resoLabel->setFont (Font (15.00f, Font::plain)); | |||
m_resoLabel->setJustificationType (Justification::centred); | |||
m_resoLabel->setEditable (false, false, false); | |||
m_resoLabel->setColour (Label::textColourId, Colour (0xffff8917)); | |||
m_resoLabel->setColour (TextEditor::textColourId, Colours::black); | |||
m_resoLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000)); | |||
addAndMakeVisible (m_driveLabel = new Label ("Drive Label", | |||
TRANS("DRIVE"))); | |||
m_driveLabel->setFont (Font (15.00f, Font::plain)); | |||
m_driveLabel->setJustificationType (Justification::centred); | |||
m_driveLabel->setEditable (false, false, false); | |||
m_driveLabel->setColour (Label::textColourId, Colour (0xffff8917)); | |||
m_driveLabel->setColour (TextEditor::textColourId, Colours::black); | |||
m_driveLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000)); | |||
addAndMakeVisible (m_curveLabel = new Label ("Curve Label", | |||
TRANS("CURVE"))); | |||
m_curveLabel->setFont (Font (15.00f, Font::plain)); | |||
m_curveLabel->setJustificationType (Justification::centred); | |||
m_curveLabel->setEditable (false, false, false); | |||
m_curveLabel->setColour (Label::textColourId, Colour (0xffff8917)); | |||
m_curveLabel->setColour (TextEditor::textColourId, Colours::black); | |||
m_curveLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000)); | |||
addAndMakeVisible (m_satLabel = new Label ("Saturation Label", | |||
TRANS("SATURATION"))); | |||
m_satLabel->setFont (Font (15.00f, Font::plain)); | |||
m_satLabel->setJustificationType (Justification::centred); | |||
m_satLabel->setEditable (false, false, false); | |||
m_satLabel->setColour (Label::textColourId, Colour (0xffff8917)); | |||
m_satLabel->setColour (TextEditor::textColourId, Colours::black); | |||
m_satLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000)); | |||
addAndMakeVisible (m_feedbackLabel = new Label ("Feedback Label", | |||
TRANS("FEEDBACK"))); | |||
m_feedbackLabel->setFont (Font (15.00f, Font::plain)); | |||
m_feedbackLabel->setJustificationType (Justification::centred); | |||
m_feedbackLabel->setEditable (false, false, false); | |||
m_feedbackLabel->setColour (Label::textColourId, Colour (0xffff8917)); | |||
m_feedbackLabel->setColour (TextEditor::textColourId, Colours::black); | |||
m_feedbackLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000)); | |||
addAndMakeVisible (m_gainLabel = new Label ("Gain Label", | |||
TRANS("LEVEL"))); | |||
m_gainLabel->setFont (Font (15.00f, Font::plain)); | |||
m_gainLabel->setJustificationType (Justification::centred); | |||
m_gainLabel->setEditable (false, false, false); | |||
m_gainLabel->setColour (Label::textColourId, Colour (0xffff8917)); | |||
m_gainLabel->setColour (TextEditor::textColourId, Colours::black); | |||
m_gainLabel->setColour (TextEditor::backgroundColourId, Colour (0x00000000)); | |||
drawable1 = Drawable::createFromImageData (BinaryData::Background_png, BinaryData::Background_pngSize); | |||
//[UserPreSize] | |||
//[/UserPreSize] | |||
setSize (744, 476); | |||
//[Constructor] You can add your own custom stuff here.. | |||
filterFreqAttachment = new SliderAttachment(m_vts, "Cutoff", *m_cutoffSlider); | |||
filterResoAttachment = new SliderAttachment(m_vts, "Resonance", *m_resoSlider); | |||
driveAttachment = new SliderAttachment(m_vts, "Drive", *m_driveSlider); | |||
curveAttachment = new SliderAttachment(m_vts, "Curve", *m_curveSlider); | |||
satAttachment = new SliderAttachment(m_vts, "Saturation", *m_satSlider); | |||
feedbackAttachment = new SliderAttachment(m_vts, "Feedback", *m_feedbackSlider); | |||
levelAttachment = new SliderAttachment(m_vts, "Level", *m_gainSlider); | |||
m_cutoffSlider->setTextValueSuffix("Hz"); | |||
m_cutoffSlider->setSkewFactorFromMidPoint(800.0); | |||
m_feedbackSlider->setTextValueSuffix("dB"); | |||
m_gainSlider->setTextValueSuffix("dB"); | |||
//[/Constructor] | |||
} | |||
MainComponent::~MainComponent() | |||
{ | |||
//[Destructor_pre]. You can add your own custom destruction code here.. | |||
// This has to happen before we null out the Sliders themselves because | |||
// when the ScopedPointer<> delete policy tries to remove Listeners from | |||
// the SliderAttachment it refers to the Sliders themselves. If the Sliders | |||
// have already been deleted at that point then we get a null pointer error. | |||
filterFreqAttachment = nullptr; | |||
filterResoAttachment = nullptr; | |||
driveAttachment = nullptr; | |||
curveAttachment = nullptr; | |||
satAttachment = nullptr; | |||
feedbackAttachment = nullptr; | |||
levelAttachment = nullptr; | |||
//[/Destructor_pre] | |||
m_cutoffSlider = nullptr; | |||
m_resoSlider = nullptr; | |||
m_driveSlider = nullptr; | |||
m_curveSlider = nullptr; | |||
m_satSlider = nullptr; | |||
m_feedbackSlider = nullptr; | |||
m_gainSlider = nullptr; | |||
m_cutoffLabel = nullptr; | |||
m_resoLabel = nullptr; | |||
m_driveLabel = nullptr; | |||
m_curveLabel = nullptr; | |||
m_satLabel = nullptr; | |||
m_feedbackLabel = nullptr; | |||
m_gainLabel = nullptr; | |||
drawable1 = nullptr; | |||
//[Destructor]. You can add your own custom destruction code here.. | |||
//[/Destructor] | |||
} | |||
//============================================================================== | |||
void MainComponent::paint (Graphics& g) | |||
{ | |||
//[UserPrePaint] Add your own custom painting code here.. | |||
//[/UserPrePaint] | |||
g.setColour (Colours::black); | |||
jassert (drawable1 != 0); | |||
if (drawable1 != 0) | |||
drawable1->drawWithin (g, Rectangle<float> (0, 0, 744, 476), | |||
RectanglePlacement::stretchToFit, 1.000f); | |||
//[UserPaint] Add your own custom painting code here.. | |||
//[/UserPaint] | |||
} | |||
void MainComponent::resized() | |||
{ | |||
//[UserPreResize] Add your own custom resize code here.. | |||
//[/UserPreResize] | |||
m_cutoffSlider->setBounds (28, 149, 72, 72); | |||
m_resoSlider->setBounds (28, 277, 72, 72); | |||
m_driveSlider->setBounds (336, 357, 72, 72); | |||
m_curveSlider->setBounds (219, 370, 50, 50); | |||
m_satSlider->setBounds (475, 370, 50, 50); | |||
m_feedbackSlider->setBounds (640, 148, 72, 72); | |||
m_gainSlider->setBounds (640, 277, 72, 72); | |||
m_cutoffLabel->setBounds (24, 111, 80, 20); | |||
m_resoLabel->setBounds (24, 369, 80, 20); | |||
m_driveLabel->setBounds (332, 450, 80, 20); | |||
m_curveLabel->setBounds (204, 440, 80, 20); | |||
m_satLabel->setBounds (462, 440, 80, 20); | |||
m_feedbackLabel->setBounds (637, 111, 80, 20); | |||
m_gainLabel->setBounds (639, 369, 80, 20); | |||
//[UserResized] Add your own custom resize handling here.. | |||
//[/UserResized] | |||
} | |||
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here... | |||
//[/MiscUserCode] | |||
//============================================================================== | |||
#if 0 | |||
/* -- Projucer information section -- | |||
This is where the Projucer stores the metadata that describe this GUI layout, so | |||
make changes in here at your peril! | |||
BEGIN_JUCER_METADATA | |||
<JUCER_COMPONENT documentType="Component" className="MainComponent" componentName="" | |||
parentClasses="public Component" constructorParams="AudioProcessorValueTreeState& vts" | |||
variableInitialisers="m_vts(vts)" snapPixels="8" snapActive="1" | |||
snapShown="1" overlayOpacity="0.330" fixedSize="1" initialWidth="744" | |||
initialHeight="476"> | |||
<BACKGROUND backgroundColour="ffffff"> | |||
<IMAGE pos="0 0 744 476" resource="BinaryData::Background_png" opacity="1" | |||
mode="0"/> | |||
</BACKGROUND> | |||
<SLIDER name="Cutoff" id="80edd5c38c704dd5" memberName="m_cutoffSlider" | |||
virtualName="" explicitFocusOrder="0" pos="28 149 72 72" min="100" | |||
max="20000" int="0" style="RotaryHorizontalVerticalDrag" textBoxPos="NoTextBox" | |||
textBoxEditable="0" textBoxWidth="80" textBoxHeight="20" skewFactor="1" | |||
needsCallback="0"/> | |||
<SLIDER name="Resonance" id="3bb5cf0ab68e9733" memberName="m_resoSlider" | |||
virtualName="" explicitFocusOrder="0" pos="28 277 72 72" min="1" | |||
max="8" int="0" style="RotaryHorizontalVerticalDrag" textBoxPos="NoTextBox" | |||
textBoxEditable="0" textBoxWidth="80" textBoxHeight="20" skewFactor="1" | |||
needsCallback="0"/> | |||
<SLIDER name="Drive" id="9909e31099819864" memberName="m_driveSlider" | |||
virtualName="" explicitFocusOrder="0" pos="336 357 72 72" min="-10" | |||
max="10" int="0" style="RotaryHorizontalVerticalDrag" textBoxPos="NoTextBox" | |||
textBoxEditable="0" textBoxWidth="80" textBoxHeight="20" skewFactor="1" | |||
needsCallback="0"/> | |||
<SLIDER name="Curve" id="a56ea73f883bdf8f" memberName="m_curveSlider" | |||
virtualName="" explicitFocusOrder="0" pos="219 370 50 50" min="0.10000000000000000555" | |||
max="4" int="0" style="RotaryHorizontalVerticalDrag" textBoxPos="NoTextBox" | |||
textBoxEditable="0" textBoxWidth="80" textBoxHeight="20" skewFactor="1" | |||
needsCallback="0"/> | |||
<SLIDER name="Saturation" id="a1e434d9a7eda8a0" memberName="m_satSlider" | |||
virtualName="" explicitFocusOrder="0" pos="475 370 50 50" min="0" | |||
max="1" int="0" style="RotaryHorizontalVerticalDrag" textBoxPos="NoTextBox" | |||
textBoxEditable="0" textBoxWidth="80" textBoxHeight="20" skewFactor="1" | |||
needsCallback="0"/> | |||
<SLIDER name="Feedback" id="ecd475fce33f4b83" memberName="m_feedbackSlider" | |||
virtualName="" explicitFocusOrder="0" pos="640 148 72 72" min="-60" | |||
max="-24" int="0" style="RotaryHorizontalVerticalDrag" textBoxPos="NoTextBox" | |||
textBoxEditable="0" textBoxWidth="80" textBoxHeight="20" skewFactor="1" | |||
needsCallback="0"/> | |||
<SLIDER name="Level" id="bf47d1ef820213ea" memberName="m_gainSlider" | |||
virtualName="" explicitFocusOrder="0" pos="640 277 72 72" min="-24" | |||
max="24" int="0" style="RotaryHorizontalVerticalDrag" textBoxPos="NoTextBox" | |||
textBoxEditable="0" textBoxWidth="80" textBoxHeight="20" skewFactor="1" | |||
needsCallback="0"/> | |||
<LABEL name="Cutoff Label" id="8c36484c7dd57b99" memberName="m_cutoffLabel" | |||
virtualName="" explicitFocusOrder="0" pos="24 111 80 20" textCol="ffff8917" | |||
edTextCol="ff000000" edBkgCol="0" labelText="CUTOFF" editableSingleClick="0" | |||
editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font" | |||
fontsize="15" bold="0" italic="0" justification="36"/> | |||
<LABEL name="Resonance Label" id="cc33e12b86f2a86a" memberName="m_resoLabel" | |||
virtualName="" explicitFocusOrder="0" pos="24 369 80 20" textCol="ffff8917" | |||
edTextCol="ff000000" edBkgCol="0" labelText="RESONANCE" editableSingleClick="0" | |||
editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font" | |||
fontsize="15" bold="0" italic="0" justification="36"/> | |||
<LABEL name="Drive Label" id="195ded46976233cb" memberName="m_driveLabel" | |||
virtualName="" explicitFocusOrder="0" pos="332 450 80 20" textCol="ffff8917" | |||
edTextCol="ff000000" edBkgCol="0" labelText="DRIVE" editableSingleClick="0" | |||
editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font" | |||
fontsize="15" bold="0" italic="0" justification="36"/> | |||
<LABEL name="Curve Label" id="30073a37efa500cc" memberName="m_curveLabel" | |||
virtualName="" explicitFocusOrder="0" pos="204 440 80 20" textCol="ffff8917" | |||
edTextCol="ff000000" edBkgCol="0" labelText="CURVE" editableSingleClick="0" | |||
editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font" | |||
fontsize="15" bold="0" italic="0" justification="36"/> | |||
<LABEL name="Saturation Label" id="e8a8527f3f6976cf" memberName="m_satLabel" | |||
virtualName="" explicitFocusOrder="0" pos="462 440 80 20" textCol="ffff8917" | |||
edTextCol="ff000000" edBkgCol="0" labelText="SATURATION" editableSingleClick="0" | |||
editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font" | |||
fontsize="15" bold="0" italic="0" justification="36"/> | |||
<LABEL name="Feedback Label" id="5547950683b7ca1f" memberName="m_feedbackLabel" | |||
virtualName="" explicitFocusOrder="0" pos="637 111 80 20" textCol="ffff8917" | |||
edTextCol="ff000000" edBkgCol="0" labelText="FEEDBACK" editableSingleClick="0" | |||
editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font" | |||
fontsize="15" bold="0" italic="0" justification="36"/> | |||
<LABEL name="Gain Label" id="719d91e31d43fa34" memberName="m_gainLabel" | |||
virtualName="" explicitFocusOrder="0" pos="639 369 80 20" textCol="ffff8917" | |||
edTextCol="ff000000" edBkgCol="0" labelText="LEVEL" editableSingleClick="0" | |||
editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font" | |||
fontsize="15" bold="0" italic="0" justification="36"/> | |||
</JUCER_COMPONENT> | |||
END_JUCER_METADATA | |||
*/ | |||
#endif | |||
//[EndFile] You can add extra defines here... | |||
//[/EndFile] |
@@ -0,0 +1,90 @@ | |||
/* | |||
============================================================================== | |||
This is an automatically generated GUI class created by the Projucer! | |||
Be careful when adding custom code to these files, as only the code within | |||
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded | |||
and re-saved. | |||
Created with Projucer version: 4.3.0 | |||
------------------------------------------------------------------------------ | |||
The Projucer is part of the JUCE library - "Jules' Utility Class Extensions" | |||
Copyright (c) 2015 - ROLI Ltd. | |||
============================================================================== | |||
*/ | |||
#ifndef __JUCE_HEADER_9002020A4DD09B20__ | |||
#define __JUCE_HEADER_9002020A4DD09B20__ | |||
//[Headers] -- You can add your own extra header files here -- | |||
#include "JuceHeader.h" | |||
//[/Headers] | |||
//============================================================================== | |||
/** | |||
//[Comments] | |||
An auto-generated component, created by the Projucer. | |||
Describe your class and how it works here! | |||
//[/Comments] | |||
*/ | |||
class MainComponent : public Component | |||
{ | |||
public: | |||
//============================================================================== | |||
MainComponent (AudioProcessorValueTreeState& vts); | |||
~MainComponent(); | |||
//============================================================================== | |||
//[UserMethods] -- You can add your own custom methods in this section. | |||
//[/UserMethods] | |||
void paint (Graphics& g) override; | |||
void resized() override; | |||
private: | |||
//[UserVariables] -- You can add your own custom variables in this section. | |||
AudioProcessorValueTreeState& m_vts; | |||
ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> filterFreqAttachment; | |||
ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> filterResoAttachment; | |||
ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> driveAttachment; | |||
ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> satAttachment; | |||
ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> curveAttachment; | |||
ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> feedbackAttachment; | |||
ScopedPointer<AudioProcessorValueTreeState::SliderAttachment> levelAttachment; | |||
//[/UserVariables] | |||
//============================================================================== | |||
ScopedPointer<Slider> m_cutoffSlider; | |||
ScopedPointer<Slider> m_resoSlider; | |||
ScopedPointer<Slider> m_driveSlider; | |||
ScopedPointer<Slider> m_curveSlider; | |||
ScopedPointer<Slider> m_satSlider; | |||
ScopedPointer<Slider> m_feedbackSlider; | |||
ScopedPointer<Slider> m_gainSlider; | |||
ScopedPointer<Label> m_cutoffLabel; | |||
ScopedPointer<Label> m_resoLabel; | |||
ScopedPointer<Label> m_driveLabel; | |||
ScopedPointer<Label> m_curveLabel; | |||
ScopedPointer<Label> m_satLabel; | |||
ScopedPointer<Label> m_feedbackLabel; | |||
ScopedPointer<Label> m_gainLabel; | |||
ScopedPointer<Drawable> drawable1; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent) | |||
}; | |||
//[EndFile] You can add extra defines here... | |||
//[/EndFile] | |||
#endif // __JUCE_HEADER_9002020A4DD09B20__ |
@@ -0,0 +1,60 @@ | |||
/* | |||
============================================================================== | |||
This file was auto-generated! | |||
It contains the basic framework code for a JUCE plugin editor. | |||
============================================================================== | |||
*/ | |||
#include "PluginProcessor.h" | |||
#include "PluginEditor.h" | |||
//============================================================================== | |||
TemperAudioProcessorEditor::TemperAudioProcessorEditor (TemperAudioProcessor& p, AudioProcessorValueTreeState& vts) | |||
: AudioProcessorEditor (&p), processor (p), m_vts(vts) | |||
{ | |||
addAndMakeVisible(m_main = new MainComponent(m_vts)); | |||
addAndMakeVisible(m_vizPre = new SpectroscopeComponent()); | |||
addAndMakeVisible(m_vizPost = new SpectroscopeComponent()); | |||
m_main->setAlwaysOnTop(true); | |||
m_vizPre->setColours(Colour::fromRGBA(255, 51, 34, 255), | |||
Colour::fromRGBA(223, 19, 19, 255).withAlpha(0.7f), | |||
Colour::fromRGBA(123, 0, 0, 255).withAlpha(0.7f)); | |||
m_vizPost->setColours(Colour::fromRGBA(255, 186, 34, 255), | |||
Colour::fromRGBA(253, 174, 25, 255).withAlpha(0.7f), | |||
Colour::fromRGBA(255, 126, 0, 255).withAlpha(0.7f)); | |||
m_vizPre->toBehind(m_vizPost); | |||
m_glContext.setComponentPaintingEnabled(true); | |||
m_glContext.attachTo(*this); | |||
setSize (744, 476); | |||
setLookAndFeel(&laf); | |||
} | |||
TemperAudioProcessorEditor::~TemperAudioProcessorEditor() | |||
{ | |||
m_glContext.detach(); | |||
setLookAndFeel(nullptr); | |||
} | |||
//============================================================================== | |||
void TemperAudioProcessorEditor::paint (Graphics& g) | |||
{ | |||
Image graphBackground = ImageCache::getFromMemory(BinaryData::GraphBackground_png, | |||
BinaryData::GraphBackground_pngSize); | |||
g.drawImageAt(graphBackground.rescaled(396, 134), 194, 181); | |||
} | |||
void TemperAudioProcessorEditor::resized() | |||
{ | |||
m_main->setBounds(0, 0, 744, 476); | |||
m_vizPre->setBounds(194, 181, 396, 134); | |||
m_vizPost->setBounds(194, 181, 396, 134); | |||
} |
@@ -0,0 +1,54 @@ | |||
/* | |||
============================================================================== | |||
This file was auto-generated! | |||
It contains the basic framework code for a JUCE plugin editor. | |||
============================================================================== | |||
*/ | |||
#ifndef PLUGINEDITOR_H_INCLUDED | |||
#define PLUGINEDITOR_H_INCLUDED | |||
#include "JuceHeader.h" | |||
#include "TemperLookAndFeel.h" | |||
#include "PluginProcessor.h" | |||
#include "MainComponent.h" | |||
#include "SpectroscopeComponent.h" | |||
typedef AudioProcessorValueTreeState::SliderAttachment SliderAttachment; | |||
//============================================================================== | |||
/** | |||
*/ | |||
class TemperAudioProcessorEditor : public AudioProcessorEditor | |||
{ | |||
public: | |||
TemperAudioProcessorEditor (TemperAudioProcessor&, AudioProcessorValueTreeState&); | |||
~TemperAudioProcessorEditor(); | |||
//============================================================================== | |||
void paint (Graphics&) override; | |||
void resized() override; | |||
//============================================================================== | |||
ScopedPointer<SpectroscopeComponent> m_vizPre; | |||
ScopedPointer<SpectroscopeComponent> m_vizPost; | |||
private: | |||
// This reference is provided as a quick way for your editor to | |||
// access the processor object that created it. | |||
TemperAudioProcessor& processor; | |||
TemperLookAndFeel laf; | |||
OpenGLContext m_glContext; | |||
AudioProcessorValueTreeState& m_vts; | |||
ScopedPointer<MainComponent> m_main; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemperAudioProcessorEditor) | |||
}; | |||
#endif // PLUGINEDITOR_H_INCLUDED |
@@ -0,0 +1,283 @@ | |||
/* | |||
============================================================================== | |||
This file was auto-generated! | |||
It contains the basic framework code for a JUCE plugin processor. | |||
============================================================================== | |||
*/ | |||
#include "PluginProcessor.h" | |||
#include "PluginEditor.h" | |||
#include "TemperDsp.hpp" | |||
const int kOversampleFactor = 3; | |||
//============================================================================== | |||
TemperAudioProcessor::TemperAudioProcessor() | |||
#ifndef JucePlugin_PreferredChannelConfigurations | |||
: AudioProcessor (BusesProperties() | |||
#if ! JucePlugin_IsMidiEffect | |||
#if ! JucePlugin_IsSynth | |||
.withInput ("Input", AudioChannelSet::stereo(), true) | |||
#endif | |||
.withOutput ("Output", AudioChannelSet::stereo(), true) | |||
#endif | |||
), | |||
m_params (*this, nullptr) | |||
#else | |||
: m_params (*this, nullptr) | |||
#endif | |||
{ | |||
m_restriction = new RestrictionProcessor(); | |||
m_bridge = new FaustUIBridge(m_params); | |||
m_lastKnownSampleRate = 0.0; | |||
m_currentProgram = -1; | |||
// Initialize the dsp units | |||
for (int i = 0; i < getTotalNumInputChannels(); ++i) | |||
{ | |||
TemperDsp* dsp = new TemperDsp(); | |||
dsp->buildUserInterface(m_bridge); | |||
m_dsps.add(dsp); | |||
} | |||
// Initialize the AudioProcessorValueTreeState root | |||
ValueTree root (Identifier("TEMPER")); | |||
m_params.state = root; | |||
} | |||
TemperAudioProcessor::~TemperAudioProcessor() | |||
{ | |||
} | |||
//============================================================================== | |||
const String TemperAudioProcessor::getName() const | |||
{ | |||
return JucePlugin_Name; | |||
} | |||
bool TemperAudioProcessor::acceptsMidi() const | |||
{ | |||
#if JucePlugin_WantsMidiInput | |||
return true; | |||
#else | |||
return false; | |||
#endif | |||
} | |||
bool TemperAudioProcessor::producesMidi() const | |||
{ | |||
#if JucePlugin_ProducesMidiOutput | |||
return true; | |||
#else | |||
return false; | |||
#endif | |||
} | |||
double TemperAudioProcessor::getTailLengthSeconds() const | |||
{ | |||
return 0.0; | |||
} | |||
int TemperAudioProcessor::getNumPrograms() | |||
{ | |||
return 5; // NB: some hosts don't cope very well if you tell them there are 0 programs, | |||
// so this should be at least 1, even if you're not really implementing programs. | |||
} | |||
int TemperAudioProcessor::getCurrentProgram() | |||
{ | |||
return m_currentProgram; | |||
} | |||
void TemperAudioProcessor::setCurrentProgram (int index) | |||
{ | |||
switch (index) { | |||
case 0: | |||
setStateInformation(BinaryData::DefaultPreset_xml, | |||
BinaryData::DefaultPreset_xmlSize); | |||
break; | |||
case 1: | |||
setStateInformation(BinaryData::StubbedToePreset_xml, | |||
BinaryData::StubbedToePreset_xmlSize); | |||
break; | |||
case 2: | |||
setStateInformation(BinaryData::BeeStingPreset_xml, | |||
BinaryData::BeeStingPreset_xmlSize); | |||
break; | |||
case 3: | |||
setStateInformation(BinaryData::MorningAtTheDMVPreset_xml, | |||
BinaryData::MorningAtTheDMVPreset_xmlSize); | |||
break; | |||
case 4: | |||
setStateInformation(BinaryData::FlyingUnitedPreset_xml, | |||
BinaryData::FlyingUnitedPreset_xmlSize); | |||
break; | |||
default: | |||
break; | |||
} | |||
m_currentProgram = index; | |||
} | |||
const String TemperAudioProcessor::getProgramName (int index) | |||
{ | |||
switch (index) { | |||
case 0: | |||
return String("Default"); | |||
case 1: | |||
return String("Stubbed Toe"); | |||
case 2: | |||
return String("Bee Sting"); | |||
case 3: | |||
return String("Morning at the DMV"); | |||
case 4: | |||
return String("Flying United"); | |||
default: | |||
return String(); | |||
} | |||
} | |||
void TemperAudioProcessor::changeProgramName (int index, const String& newName) | |||
{ | |||
} | |||
//============================================================================== | |||
void TemperAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) | |||
{ | |||
auto filterType = juce::dsp::Oversampling<float>::filterHalfBandPolyphaseIIR; | |||
m_oversampler = std::unique_ptr<juce::dsp::Oversampling<float>>(new juce::dsp::Oversampling<float>(getTotalNumInputChannels(), | |||
kOversampleFactor, filterType, false)); | |||
// Re-initialize the dsp modules at the upsampled rate. | |||
if (m_lastKnownSampleRate == 0.0) | |||
for (int i = 0; i < m_dsps.size(); ++i) | |||
m_dsps.getUnchecked(i)->init(sampleRate * pow(2, kOversampleFactor)); | |||
else | |||
for (int i = 0; i < m_dsps.size(); ++i) | |||
m_dsps.getUnchecked(i)->instanceConstants(sampleRate * pow(2, kOversampleFactor)); | |||
m_oversampler->initProcessing(static_cast<size_t> (samplesPerBlock)); | |||
m_restriction->prepareToPlay(samplesPerBlock, sampleRate); | |||
m_lastKnownSampleRate = sampleRate; | |||
setLatencySamples(static_cast<int>(m_oversampler->getLatencyInSamples())); | |||
} | |||
void TemperAudioProcessor::releaseResources() | |||
{ | |||
// When playback stops, you can use this as an opportunity to free up any | |||
// spare memory, etc. | |||
} | |||
#ifndef JucePlugin_PreferredChannelConfigurations | |||
bool TemperAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const | |||
{ | |||
#if JucePlugin_IsMidiEffect | |||
ignoreUnused (layouts); | |||
return true; | |||
#else | |||
// This is the place where you check if the layout is supported. | |||
// In this template code we only support mono or stereo. | |||
if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono() | |||
&& layouts.getMainOutputChannelSet() != AudioChannelSet::stereo()) | |||
return false; | |||
// This checks if the input layout matches the output layout | |||
#if ! JucePlugin_IsSynth | |||
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) | |||
return false; | |||
#endif | |||
return true; | |||
#endif | |||
} | |||
#endif | |||
void TemperAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) | |||
{ | |||
const int totalNumInputChannels = getTotalNumInputChannels(); | |||
const int totalNumOutputChannels = getTotalNumOutputChannels(); | |||
TemperAudioProcessorEditor* editor = static_cast<TemperAudioProcessorEditor*>(getActiveEditor()); | |||
// In case we have more outputs than inputs, this code clears any output | |||
// channels that didn't contain input data, (because these aren't | |||
// guaranteed to be empty - they may contain garbage). | |||
// This is here to avoid people getting screaming feedback | |||
// when they first compile a plugin, but obviously you don't need to keep | |||
// this code if your algorithm always overwrites all the output channels. | |||
for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i) | |||
buffer.clear (i, 0, buffer.getNumSamples()); | |||
// Push input buffer into the Pre spectroscope component. | |||
if (editor) | |||
editor->m_vizPre->pushBuffer(buffer); | |||
// Now the guts of the processing; oversampling and applying the Faust dsp module. | |||
const int numInputChannels = buffer.getNumChannels(); | |||
const int numInputSamples = buffer.getNumSamples(); | |||
juce::dsp::AudioBlock<float> block (buffer.getArrayOfWritePointers(), | |||
numInputChannels, | |||
numInputSamples); | |||
juce::dsp::AudioBlock<float> oversampledBlock = m_oversampler->processSamplesUp(block); | |||
// Run the faust processors on each channel of the oversampled block. | |||
for (int i = 0; i < numInputChannels; ++i) | |||
{ | |||
auto* processor = m_dsps.getUnchecked(i); | |||
auto* data = oversampledBlock.getChannelPointer(i); | |||
int len = static_cast<int>(oversampledBlock.getNumSamples()); | |||
processor->compute(len, &data, &data); | |||
} | |||
m_oversampler->processSamplesDown(block); | |||
#ifdef TEMPER_DEMO_BUILD | |||
// After the Faust processing, add the demo restriction to the output stream | |||
m_restriction->processBlock(buffer); | |||
#endif | |||
// Push resulting buffer into the Post spectroscope component. | |||
if (editor) | |||
editor->m_vizPost->pushBuffer(buffer); | |||
} | |||
//============================================================================== | |||
bool TemperAudioProcessor::hasEditor() const | |||
{ | |||
return true; // (change this to false if you choose to not supply an editor) | |||
} | |||
AudioProcessorEditor* TemperAudioProcessor::createEditor() | |||
{ | |||
return new TemperAudioProcessorEditor (*this, m_params); | |||
} | |||
//============================================================================== | |||
void TemperAudioProcessor::getStateInformation (MemoryBlock& destData) | |||
{ | |||
ScopedPointer<XmlElement> xml (m_params.state.createXml()); | |||
copyXmlToBinary(*xml, destData); | |||
} | |||
void TemperAudioProcessor::setStateInformation (const void* data, int sizeInBytes) | |||
{ | |||
ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes)); | |||
if (xmlState != nullptr) | |||
if (xmlState->hasTagName (m_params.state.getType())) | |||
m_params.state = ValueTree::fromXml (*xmlState); | |||
} | |||
//============================================================================== | |||
// This creates new instances of the plugin.. | |||
AudioProcessor* JUCE_CALLTYPE createPluginFilter() | |||
{ | |||
return new TemperAudioProcessor(); | |||
} |
@@ -0,0 +1,79 @@ | |||
/* | |||
============================================================================== | |||
This file was auto-generated! | |||
It contains the basic framework code for a JUCE plugin processor. | |||
============================================================================== | |||
*/ | |||
#ifndef PLUGINPROCESSOR_H_INCLUDED | |||
#define PLUGINPROCESSOR_H_INCLUDED | |||
#include "JuceHeader.h" | |||
#include "FaustUIBridge.h" | |||
#include "RestrictionProcessor.h" | |||
#include "faust/dsp/dsp.h" | |||
//============================================================================== | |||
/** | |||
*/ | |||
class TemperAudioProcessor : public AudioProcessor | |||
{ | |||
public: | |||
//============================================================================== | |||
TemperAudioProcessor(); | |||
~TemperAudioProcessor(); | |||
//============================================================================== | |||
void prepareToPlay (double sampleRate, int samplesPerBlock) override; | |||
void releaseResources() override; | |||
#ifndef JucePlugin_PreferredChannelConfigurations | |||
bool isBusesLayoutSupported (const BusesLayout& layouts) const override; | |||
#endif | |||
void processBlock (AudioSampleBuffer&, MidiBuffer&) override; | |||
//============================================================================== | |||
AudioProcessorEditor* createEditor() override; | |||
bool hasEditor() const override; | |||
//============================================================================== | |||
const String getName() const override; | |||
bool acceptsMidi() const override; | |||
bool producesMidi() const override; | |||
double getTailLengthSeconds() const override; | |||
//============================================================================== | |||
int getNumPrograms() override; | |||
int getCurrentProgram() override; | |||
void setCurrentProgram (int index) override; | |||
const String getProgramName (int index) override; | |||
void changeProgramName (int index, const String& newName) override; | |||
//============================================================================== | |||
void getStateInformation (MemoryBlock& destData) override; | |||
void setStateInformation (const void* data, int sizeInBytes) override; | |||
private: | |||
AudioProcessorValueTreeState m_params; | |||
OwnedArray<::dsp> m_dsps; | |||
ScopedPointer<FaustUIBridge> m_bridge; | |||
ScopedPointer<RestrictionProcessor> m_restriction; | |||
std::unique_ptr<juce::dsp::Oversampling<float>> m_oversampler; | |||
double m_lastKnownSampleRate; | |||
int m_currentProgram; | |||
//============================================================================== | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemperAudioProcessor) | |||
}; | |||
#endif // PLUGINPROCESSOR_H_INCLUDED |
@@ -0,0 +1,55 @@ | |||
/* | |||
============================================================================== | |||
RestrictionProcessor.cpp | |||
Created: 15 Feb 2017 10:03:51pm | |||
Author: Nick Thompson | |||
============================================================================== | |||
*/ | |||
#include "RestrictionProcessor.h" | |||
const double kFreq = 0.05; // Twenty second period. | |||
const double kDuty = 0.75; // Fifteen seconds of audio, five seconds of silence. | |||
//============================================================================== | |||
RestrictionProcessor::RestrictionProcessor() | |||
{ | |||
m_smoothing = new LinearSmoothedValue<float>(); | |||
} | |||
//============================================================================== | |||
void RestrictionProcessor::prepareToPlay(int samplesPerBlockExpected, double sampleRate) | |||
{ | |||
m_sampleRate = sampleRate; | |||
m_delta = kFreq / m_sampleRate; | |||
m_currentAngle = 0.0; | |||
m_smoothing->reset(sampleRate, 1.0); | |||
} | |||
void RestrictionProcessor::releaseResources() {} | |||
void RestrictionProcessor::processBlock(AudioSampleBuffer &buffer) | |||
{ | |||
const int numChannels = buffer.getNumChannels(); | |||
const int numSamples = buffer.getNumSamples(); | |||
float** channelData = buffer.getArrayOfWritePointers(); | |||
for (int j = 0; j < numSamples; ++j) | |||
{ | |||
const float targetGain = static_cast<float>(m_currentAngle <= kDuty); | |||
m_smoothing->setValue(targetGain); | |||
const float gain = m_smoothing->getNextValue(); | |||
for (int i = 0; i < numChannels; ++i) | |||
{ | |||
const float in = channelData[i][j]; | |||
channelData[i][j] = gain * in; | |||
} | |||
m_currentAngle = fmod(m_currentAngle + m_delta, 1.0); | |||
} | |||
} |
@@ -0,0 +1,66 @@ | |||
/* | |||
============================================================================== | |||
RestrictionProcessor.h | |||
Created: 15 Feb 2017 10:03:51pm | |||
Author: Nick Thompson | |||
============================================================================== | |||
*/ | |||
#ifndef RESTRICTIONPROCESSOR_H_INCLUDED | |||
#define RESTRICTIONPROCESSOR_H_INCLUDED | |||
#include "JuceHeader.h" | |||
//============================================================================== | |||
/** | |||
A simple variant on the JUCE AudioSource for managing the trial version | |||
restrictions and plugin validation. | |||
When a source needs to be played, it is first put into a 'prepared' state by a call to | |||
prepareToPlay(), and then repeated calls will be made to its processBlock() method to | |||
process the audio data. | |||
*/ | |||
class RestrictionProcessor | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Creates an AudioSource. */ | |||
RestrictionProcessor(); | |||
/** Destructor. */ | |||
virtual ~RestrictionProcessor() {} | |||
//============================================================================== | |||
/** Tells the processor to prepare for playing. | |||
*/ | |||
void prepareToPlay (int samplesPerBlockExpected, double sampleRate); | |||
/** Allows the source to release anything it no longer needs after playback has stopped. | |||
This will be called when the source is no longer going to have its processBlock() | |||
method called, so it should release any spare memory, etc. that it might have | |||
allocated during the prepareToPlay() call. | |||
*/ | |||
void releaseResources(); | |||
/** Called repeatedly to fetch subsequent blocks of audio data. | |||
After calling the prepareToPlay() method, this callback will be made each | |||
time the audio playback hardware (or whatever other destination the audio | |||
data is going to) needs another block of data. | |||
*/ | |||
void processBlock (AudioSampleBuffer& buffer); | |||
private: | |||
//============================================================================== | |||
ScopedPointer<LinearSmoothedValue<float>> m_smoothing; | |||
//============================================================================== | |||
double m_sampleRate; | |||
double m_delta; | |||
double m_currentAngle; | |||
}; | |||
#endif // RESTRICTIONPROCESSOR_H_INCLUDED |
@@ -0,0 +1,131 @@ | |||
/* | |||
============================================================================== | |||
SpectroscopeComponent.cpp | |||
Created: 8 Apr 2017 12:46:51pm | |||
Author: Nick Thompson | |||
============================================================================== | |||
*/ | |||
#include "JuceHeader.h" | |||
#include "SpectroscopeComponent.h" | |||
//============================================================================== | |||
SpectroscopeComponent::SpectroscopeComponent() | |||
: m_fifoIndex(0), | |||
m_fftBlockReady(false), | |||
m_forwardFFT(kFFTOrder), | |||
m_window(kFFTSize, juce::dsp::WindowingFunction<float>::hann), | |||
m_strokeColour(Colours::white), | |||
m_fillStartColour(Colours::white.withAlpha(0.2f)), | |||
m_fillStopColour(Colours::white.withAlpha(0.8f)) | |||
{ | |||
zeromem(m_outputData, sizeof(m_outputData)); | |||
setSize(700, 200); | |||
startTimerHz(30); | |||
} | |||
SpectroscopeComponent::~SpectroscopeComponent() | |||
{ | |||
stopTimer(); | |||
} | |||
void SpectroscopeComponent::paint (Graphics& g) | |||
{ | |||
const float width = (float) getWidth(); | |||
const float height = (float) getHeight(); | |||
// Clear the drawing target | |||
g.setColour(Colours::transparentBlack); | |||
g.fillAll(); | |||
// The values in the output bins after the FFT have a range that I don't understand | |||
// and isn't explained in the docs. It seems that if I scale down by the size of the | |||
// fft buffer, I get somewhat reasonable results on the graph. But in examples I've | |||
// seen, we would just divide here by the maximum value in the bins at the time of | |||
// drawing. Seeing as that would be inconsistent between frames, I'm defaulting to the | |||
// size of the fft here unless the max value in the bins is larger. | |||
Range<float> maxBin = FloatVectorOperations::findMinAndMax(m_outputData, kOutputSize); | |||
const float scale = 1.0f / jmax((float) kFFTSize, maxBin.getEnd()); | |||
g.setColour(m_fillStartColour); | |||
for (int i = 0; i < kOutputSize; ++i) | |||
{ | |||
float x = std::log10 (1 + 39 * ((i + 1.0f) / kOutputSize)) / std::log10 (40.0f) * width; | |||
const float yMag = scale * m_outputData[i]; | |||
const float yDecibel = Decibels::gainToDecibels(yMag); | |||
const float y = jmap(yDecibel, -90.0f, -12.0f, height, 0.0f); | |||
g.drawVerticalLine((int) x, y, height); | |||
} | |||
} | |||
void SpectroscopeComponent::resized() | |||
{ | |||
} | |||
void SpectroscopeComponent::timerCallback() | |||
{ | |||
if (m_fftBlockReady) | |||
{ | |||
// Compute the frequency transform | |||
m_window.multiplyWithWindowingTable(m_fftData, kFFTSize); | |||
m_forwardFFT.performFrequencyOnlyForwardTransform(m_fftData); | |||
// Copy the frequency bins into the output data buffer, taking | |||
// max(output[i], fftData[i]) for each bin. Note that after computing the | |||
// FrequencyOnlyForwardTransform on an array A of size N, A[N/2, N) is full | |||
// of zeros, and A[0, N/4) is a mirror of A[N/4, N/2). Therefore we only copy | |||
// kFFTSize / 2 samples into the output data buffer here. | |||
FloatVectorOperations::max(m_outputData, m_outputData, m_fftData, kOutputSize); | |||
m_fftBlockReady = false; | |||
} | |||
// Decay the output bin magnitudes | |||
for (int i = 0; i < kOutputSize; ++i) | |||
m_outputData[i] *= 0.707f; | |||
repaint(); | |||
} | |||
void SpectroscopeComponent::pushBuffer(AudioSampleBuffer &buffer) | |||
{ | |||
if (buffer.getNumChannels() > 0) | |||
{ | |||
const int numSamples = buffer.getNumSamples(); | |||
const float* channelData = buffer.getReadPointer(0); | |||
for (int i = 0; i < numSamples; ++i) | |||
pushSample(channelData[i]); | |||
} | |||
} | |||
inline void SpectroscopeComponent::pushSample(float sample) | |||
{ | |||
// When we wrap around the fifo table, we copy the data into the | |||
// FFT buffer and prepare to perform the transform. | |||
if (m_fifoIndex == kFFTSize) | |||
{ | |||
if (!m_fftBlockReady) | |||
{ | |||
zeromem(m_fftData, sizeof(m_fftData)); | |||
memcpy(m_fftData, m_fifo, sizeof(m_fifo)); | |||
m_fftBlockReady = true; | |||
} | |||
m_fifoIndex = 0; | |||
} | |||
m_fifo[m_fifoIndex++] = sample; | |||
} | |||
void SpectroscopeComponent::setColours(Colour strokeColour, Colour fillStartColour, Colour fillStopColour) | |||
{ | |||
m_strokeColour = strokeColour; | |||
m_fillStartColour = fillStartColour; | |||
m_fillStopColour = fillStopColour; | |||
} |
@@ -0,0 +1,60 @@ | |||
/* | |||
============================================================================== | |||
SpectroscopeComponent.h | |||
Created: 8 Apr 2017 12:46:51pm | |||
Author: Nick Thompson | |||
============================================================================== | |||
*/ | |||
#ifndef SPECTROSCOPECOMPONENT_H_INCLUDED | |||
#define SPECTROSCOPECOMPONENT_H_INCLUDED | |||
#include "JuceHeader.h" | |||
//============================================================================== | |||
/* | |||
*/ | |||
class SpectroscopeComponent : public Component, | |||
private Timer | |||
{ | |||
public: | |||
SpectroscopeComponent(); | |||
~SpectroscopeComponent(); | |||
void paint (Graphics&) override; | |||
void resized() override; | |||
void timerCallback() override; | |||
void pushBuffer (AudioSampleBuffer& buffer); | |||
inline void pushSample (float sample); | |||
void setColours (Colour strokeColour, Colour fillStartColour, Colour fillStopColour); | |||
enum { | |||
kFFTOrder = 11, | |||
kFFTSize = 2048, // 2^11 | |||
kOutputSize = 1024, // 2048 / 2 | |||
}; | |||
private: | |||
float m_fifo [kFFTSize]; | |||
float m_fftData [2 * kFFTSize]; | |||
float m_outputData [kOutputSize]; | |||
unsigned int m_fifoIndex; | |||
bool m_fftBlockReady; | |||
juce::dsp::FFT m_forwardFFT; | |||
juce::dsp::WindowingFunction<float> m_window; | |||
Colour m_strokeColour; | |||
Colour m_fillStartColour; | |||
Colour m_fillStopColour; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SpectroscopeComponent) | |||
}; | |||
#endif // SPECTROSCOPECOMPONENT_H_INCLUDED |
@@ -0,0 +1,339 @@ | |||
/* ------------------------------------------------------------ | |||
name: "temper" | |||
Code generated with Faust 2.5.32 (https://faust.grame.fr) | |||
Compilation options: cpp, -scal -ftz 0 | |||
------------------------------------------------------------ */ | |||
#ifndef __TemperDsp_H__ | |||
#define __TemperDsp_H__ | |||
/************************************************************************ | |||
************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This is sample code. This file is provided as an example of minimal | |||
FAUST architecture file. Redistribution and use in source and binary | |||
forms, with or without modification, in part or in full are permitted. | |||
In particular you can create a derived work of this FAUST architecture | |||
and distribute that work under terms of your choice. | |||
This sample code 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. | |||
************************************************************************ | |||
************************************************************************/ | |||
#include <math.h> | |||
#include <algorithm> | |||
#include "faust/gui/UI.h" | |||
#include "faust/gui/meta.h" | |||
#include "faust/dsp/dsp.h" | |||
using std::max; | |||
using std::min; | |||
/****************************************************************************** | |||
******************************************************************************* | |||
VECTOR INTRINSICS | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
/****************************************************************************** | |||
******************************************************************************* | |||
ABSTRACT USER INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
//---------------------------------------------------------------------------- | |||
// FAUST generated signal processor | |||
//---------------------------------------------------------------------------- | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
#include <cmath> | |||
#include <math.h> | |||
float TemperDsp_faustpower2_f(float value) { | |||
return (value * value); | |||
} | |||
#ifndef FAUSTCLASS | |||
#define FAUSTCLASS TemperDsp | |||
#endif | |||
#ifdef __APPLE__ | |||
#define exp10f __exp10f | |||
#define exp10 __exp10 | |||
#endif | |||
class TemperDsp : public ::dsp { | |||
private: | |||
FAUSTFLOAT fHslider0; | |||
float fRec3[2]; | |||
FAUSTFLOAT fHslider1; | |||
float fRec4[2]; | |||
int fSamplingFreq; | |||
float fConst0; | |||
float fConst1; | |||
FAUSTFLOAT fHslider2; | |||
float fRec6[2]; | |||
FAUSTFLOAT fHslider3; | |||
float fRec7[2]; | |||
float fRec5[3]; | |||
float fConst2; | |||
float fConst3; | |||
float fRec8[2]; | |||
FAUSTFLOAT fHslider4; | |||
float fRec9[2]; | |||
FAUSTFLOAT fHslider5; | |||
float fRec10[2]; | |||
float fVec0[2]; | |||
float fRec2[2]; | |||
float fRec1[2]; | |||
float fRec0[2]; | |||
FAUSTFLOAT fHslider6; | |||
float fRec11[2]; | |||
public: | |||
void metadata(Meta* m) { | |||
m->declare("analyzers.lib/name", "Faust Analyzer Library"); | |||
m->declare("analyzers.lib/version", "0.0"); | |||
m->declare("basics.lib/name", "Faust Basic Element Library"); | |||
m->declare("basics.lib/version", "0.0"); | |||
m->declare("filename", "temper"); | |||
m->declare("filters.lib/name", "Faust Filters Library"); | |||
m->declare("filters.lib/version", "0.0"); | |||
m->declare("maths.lib/author", "GRAME"); | |||
m->declare("maths.lib/copyright", "GRAME"); | |||
m->declare("maths.lib/license", "LGPL with exception"); | |||
m->declare("maths.lib/name", "Faust Math Library"); | |||
m->declare("maths.lib/version", "2.1"); | |||
m->declare("name", "temper"); | |||
m->declare("signals.lib/name", "Faust Signal Routing Library"); | |||
m->declare("signals.lib/version", "0.0"); | |||
} | |||
virtual int getNumInputs() { | |||
return 1; | |||
} | |||
virtual int getNumOutputs() { | |||
return 1; | |||
} | |||
virtual int getInputRate(int channel) { | |||
int rate; | |||
switch (channel) { | |||
case 0: { | |||
rate = 1; | |||
break; | |||
} | |||
default: { | |||
rate = -1; | |||
break; | |||
} | |||
} | |||
return rate; | |||
} | |||
virtual int getOutputRate(int channel) { | |||
int rate; | |||
switch (channel) { | |||
case 0: { | |||
rate = 1; | |||
break; | |||
} | |||
default: { | |||
rate = -1; | |||
break; | |||
} | |||
} | |||
return rate; | |||
} | |||
static void classInit(int samplingFreq) { | |||
} | |||
virtual void instanceConstants(int samplingFreq) { | |||
fSamplingFreq = samplingFreq; | |||
fConst0 = min(192000.0f, max(1.0f, float(fSamplingFreq))); | |||
fConst1 = (3.14159274f / fConst0); | |||
fConst2 = expf((0.0f - (25.0f / fConst0))); | |||
fConst3 = (1.0f - fConst2); | |||
} | |||
virtual void instanceResetUserInterface() { | |||
fHslider0 = FAUSTFLOAT(1.0f); | |||
fHslider1 = FAUSTFLOAT(-60.0f); | |||
fHslider2 = FAUSTFLOAT(20000.0f); | |||
fHslider3 = FAUSTFLOAT(1.0f); | |||
fHslider4 = FAUSTFLOAT(4.0f); | |||
fHslider5 = FAUSTFLOAT(1.0f); | |||
fHslider6 = FAUSTFLOAT(-3.0f); | |||
} | |||
virtual void instanceClear() { | |||
for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { | |||
fRec3[l0] = 0.0f; | |||
} | |||
for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { | |||
fRec4[l1] = 0.0f; | |||
} | |||
for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { | |||
fRec6[l2] = 0.0f; | |||
} | |||
for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { | |||
fRec7[l3] = 0.0f; | |||
} | |||
for (int l4 = 0; (l4 < 3); l4 = (l4 + 1)) { | |||
fRec5[l4] = 0.0f; | |||
} | |||
for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { | |||
fRec8[l5] = 0.0f; | |||
} | |||
for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { | |||
fRec9[l6] = 0.0f; | |||
} | |||
for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { | |||
fRec10[l7] = 0.0f; | |||
} | |||
for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { | |||
fVec0[l8] = 0.0f; | |||
} | |||
for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { | |||
fRec2[l9] = 0.0f; | |||
} | |||
for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { | |||
fRec1[l10] = 0.0f; | |||
} | |||
for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { | |||
fRec0[l11] = 0.0f; | |||
} | |||
for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { | |||
fRec11[l12] = 0.0f; | |||
} | |||
} | |||
virtual void init(int samplingFreq) { | |||
classInit(samplingFreq); | |||
instanceInit(samplingFreq); | |||
} | |||
virtual void instanceInit(int samplingFreq) { | |||
instanceConstants(samplingFreq); | |||
instanceResetUserInterface(); | |||
instanceClear(); | |||
} | |||
virtual TemperDsp* clone() { | |||
return new TemperDsp(); | |||
} | |||
virtual int getSampleRate() { | |||
return fSamplingFreq; | |||
} | |||
virtual void buildUserInterface(UI* ui_interface) { | |||
ui_interface->openVerticalBox("temper"); | |||
ui_interface->addHorizontalSlider("Curve", &fHslider5, 1.0f, 0.100000001f, 4.0f, 0.00100000005f); | |||
ui_interface->addHorizontalSlider("Cutoff", &fHslider2, 20000.0f, 100.0f, 20000.0f, 1.0f); | |||
ui_interface->addHorizontalSlider("Drive", &fHslider4, 4.0f, -10.0f, 10.0f, 0.00100000005f); | |||
ui_interface->addHorizontalSlider("Feedback", &fHslider1, -60.0f, -60.0f, -24.0f, 1.0f); | |||
ui_interface->addHorizontalSlider("Level", &fHslider6, -3.0f, -24.0f, 24.0f, 1.0f); | |||
ui_interface->addHorizontalSlider("Resonance", &fHslider3, 1.0f, 1.0f, 8.0f, 0.00100000005f); | |||
ui_interface->addHorizontalSlider("Saturation", &fHslider0, 1.0f, 0.0f, 1.0f, 0.00100000005f); | |||
ui_interface->closeBox(); | |||
} | |||
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { | |||
FAUSTFLOAT* input0 = inputs[0]; | |||
FAUSTFLOAT* output0 = outputs[0]; | |||
float fSlow0 = (0.00499999989f * float(fHslider0)); | |||
float fSlow1 = (0.00499999989f * powf(10.0f, (0.0500000007f * float(fHslider1)))); | |||
float fSlow2 = (0.00499999989f / tanf((fConst1 * float(fHslider2)))); | |||
float fSlow3 = (0.00499999989f * float(fHslider3)); | |||
float fSlow4 = (0.00499999989f * float(fHslider4)); | |||
float fSlow5 = (0.00499999989f * float(fHslider5)); | |||
float fSlow6 = (0.00499999989f * powf(10.0f, (0.0500000007f * float(fHslider6)))); | |||
for (int i = 0; (i < count); i = (i + 1)) { | |||
fRec3[0] = (fSlow0 + (0.995000005f * fRec3[1])); | |||
fRec4[0] = (fSlow1 + (0.995000005f * fRec4[1])); | |||
fRec6[0] = (fSlow2 + (0.995000005f * fRec6[1])); | |||
fRec7[0] = (fSlow3 + (0.995000005f * fRec7[1])); | |||
float fTemp0 = (1.0f / fRec7[0]); | |||
float fTemp1 = ((fRec6[0] * (fRec6[0] + fTemp0)) + 1.0f); | |||
fRec5[0] = (float(input0[i]) - (((((fRec6[0] * (fRec6[0] - fTemp0)) + 1.0f) * fRec5[2]) + (2.0f * (fRec5[1] * (1.0f - TemperDsp_faustpower2_f(fRec6[0]))))) / fTemp1)); | |||
float fTemp2 = ((fRec4[0] * fRec0[1]) + (((fRec5[0] + (2.0f * fRec5[1])) + fRec5[2]) / fTemp1)); | |||
float fTemp3 = fabsf(fTemp2); | |||
fRec8[0] = max(fTemp3, ((fConst3 * fTemp3) + (fConst2 * fRec8[1]))); | |||
fRec9[0] = (fSlow4 + (0.995000005f * fRec9[1])); | |||
float fTemp4 = min(3.0f, max(-3.0f, (fRec8[0] + (fRec9[0] * fTemp2)))); | |||
fRec10[0] = (fSlow5 + (0.995000005f * fRec10[1])); | |||
float fTemp5 = TemperDsp_faustpower2_f(fRec10[0]); | |||
float fTemp6 = (TemperDsp_faustpower2_f(fTemp4) * fTemp5); | |||
float fTemp7 = ((fTemp4 * (fTemp6 + 27.0f)) * ((9.0f * fTemp5) + 27.0f)); | |||
float fTemp8 = (((9.0f * fTemp6) + 27.0f) * (fTemp5 + 27.0f)); | |||
float fTemp9 = (((1.0f - fRec3[0]) * fTemp2) + (0.239999995f * ((fTemp7 * fRec3[0]) / fTemp8))); | |||
fVec0[0] = fTemp9; | |||
fRec2[0] = (fVec0[1] + (((0.0f - (0.239999995f * (fTemp7 / fTemp8))) * fRec2[1]) + (0.239999995f * ((fTemp7 * fTemp9) / fTemp8)))); | |||
fRec1[0] = ((fRec2[0] + (0.995000005f * fRec1[1])) - fRec2[1]); | |||
fRec0[0] = fRec1[0]; | |||
fRec11[0] = (fSlow6 + (0.995000005f * fRec11[1])); | |||
output0[i] = FAUSTFLOAT((4.0f * (fRec0[0] * fRec11[0]))); | |||
fRec3[1] = fRec3[0]; | |||
fRec4[1] = fRec4[0]; | |||
fRec6[1] = fRec6[0]; | |||
fRec7[1] = fRec7[0]; | |||
fRec5[2] = fRec5[1]; | |||
fRec5[1] = fRec5[0]; | |||
fRec8[1] = fRec8[0]; | |||
fRec9[1] = fRec9[0]; | |||
fRec10[1] = fRec10[0]; | |||
fVec0[1] = fVec0[0]; | |||
fRec2[1] = fRec2[0]; | |||
fRec1[1] = fRec1[0]; | |||
fRec0[1] = fRec0[0]; | |||
fRec11[1] = fRec11[0]; | |||
} | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,96 @@ | |||
/* | |||
============================================================================== | |||
TemperLookAndFeel.cpp | |||
Created: 1 Mar 2017 8:15:09pm | |||
Author: Nick Thompson | |||
============================================================================== | |||
*/ | |||
#include "TemperLookAndFeel.h" | |||
TemperLookAndFeel::TemperLookAndFeel() | |||
{ | |||
setColour(Slider::rotarySliderFillColourId, Colour::fromRGBA(226, 115, 0, 255)); | |||
} | |||
Font TemperLookAndFeel::getBaseFont() | |||
{ | |||
return Font(Typeface::createSystemTypefaceFor(BinaryData::MontserratLight_otf, | |||
BinaryData::MontserratLight_otfSize)); | |||
} | |||
Font TemperLookAndFeel::getLabelFont(Label &) | |||
{ | |||
return getBaseFont().withPointHeight(10); | |||
} | |||
Font TemperLookAndFeel::getSliderReadoutFont() | |||
{ | |||
return getBaseFont().withPointHeight(14); | |||
} | |||
void TemperLookAndFeel::drawLabel(Graphics& g, Label& l) | |||
{ | |||
Colour labelColour = Colour::fromRGB(149, 89, 17); | |||
Font labelFont = getLabelFont(l); | |||
g.setColour(labelColour); | |||
g.setFont(labelFont); | |||
Rectangle<int> textArea (l.getBorderSize().subtractedFrom (l.getLocalBounds())); | |||
g.drawFittedText (l.getText(), textArea, l.getJustificationType(), | |||
jmax (1, (int) (textArea.getHeight() / labelFont.getHeight())), | |||
l.getMinimumHorizontalScale()); | |||
} | |||
void TemperLookAndFeel::drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos, | |||
const float rotaryStartAngle, const float rotaryEndAngle, Slider& slider) | |||
{ | |||
const float radius = jmin (width / 2, height / 2) - 2.0f; | |||
const float centreX = x + width * 0.5f; | |||
const float centreY = y + height * 0.5f; | |||
const float rx = centreX - radius; | |||
const float ry = centreY - radius; | |||
const float rw = radius * 2.0f; | |||
const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); | |||
const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); | |||
// Draw the readout | |||
Colour readoutColour = Colour::fromRGB(254, 173, 29).withAlpha(isMouseOver ? 1.0f : 0.9f); | |||
const double value = slider.getValue(); | |||
String readoutValue = (value >= 1000.0 ? String(value / 1000.0, 1) + "k" : String(value, 1)); | |||
String readout = readoutValue + slider.getTextValueSuffix(); | |||
g.setColour(readoutColour); | |||
g.setFont(getSliderReadoutFont()); | |||
g.drawText(readout, centreX - radius, centreY - 10.0f, rw, 24.0f, Justification::centred); | |||
// Draw the track | |||
g.setColour (slider.findColour (Slider::rotarySliderOutlineColourId)); | |||
Path track; | |||
track.addArc(rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, true); | |||
g.strokePath(track, PathStrokeType(3.0f)); | |||
// Draw the slider position | |||
Colour sliderFillStart = Colour::fromRGBA(245, 121, 35, 255).withAlpha(isMouseOver ? 1.0f : 0.9f); | |||
Colour sliderFillStop = Colour::fromRGBA(255, 184, 23, 255).withAlpha(isMouseOver ? 1.0f : 0.9f); | |||
ColourGradient sliderFill = ColourGradient(sliderFillStart, | |||
(float) x, | |||
(float) 0, | |||
sliderFillStop, | |||
(float) width, | |||
(float) 0, | |||
false); | |||
g.setGradientFill(sliderFill); | |||
Path filledArc; | |||
filledArc.addArc(rx, ry, rw, rw, rotaryStartAngle, angle, true); | |||
PathStrokeType(3.0f).createStrokedPath(filledArc, filledArc); | |||
g.fillPath(filledArc); | |||
} |
@@ -0,0 +1,31 @@ | |||
/* | |||
============================================================================== | |||
TemperLookAndFeel.h | |||
Created: 1 Mar 2017 8:15:08pm | |||
Author: Nick Thompson | |||
============================================================================== | |||
*/ | |||
#ifndef TEMPERLOOKANDFEEL_H_INCLUDED | |||
#define TEMPERLOOKANDFEEL_H_INCLUDED | |||
#include "JuceHeader.h" | |||
class TemperLookAndFeel : public LookAndFeel_V2 | |||
{ | |||
public: | |||
TemperLookAndFeel(); | |||
Font getBaseFont (); | |||
Font getLabelFont (Label&) override; | |||
Font getSliderReadoutFont (); | |||
void drawLabel (Graphics&, Label&) override; | |||
void drawRotarySlider (Graphics&, int x, int y, int width, int height, | |||
float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, | |||
Slider&) override; | |||
}; | |||
#endif // TEMPERLOOKANDFEEL_H_INCLUDED |
@@ -0,0 +1,449 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Audio Unit UI | |||
Copyright (C) 2013 Reza Payami | |||
All rights reserved. | |||
----------------------------BSD License------------------------------ | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions | |||
are met: | |||
* Redistributions of source code must retain the above copyright | |||
notice, this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above | |||
copyright notice, this list of conditions and the following | |||
disclaimer in the documentation and/or other materials provided | |||
with the distribution. | |||
* Neither the name of Remy Muller nor the names of its | |||
contributors may be used to endorse or promote products derived | |||
from this software without specific prior written permission. | |||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |||
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |||
OF THE POSSIBILITY OF SUCH DAMAGE. | |||
----------------------------Audio Unit SDK---------------------------------- | |||
In order to compile a AU (TM) Synth plugin with this architecture file | |||
you will need the proprietary AU SDK from Apple. Please check | |||
the corresponding license. | |||
************************************************************************/ | |||
#include <set> | |||
#include "faust/gui/UI.h" | |||
using namespace std; | |||
class auUI; | |||
class auUIObject { | |||
public: | |||
string fLabel; | |||
float* fZone; | |||
float range(float min, float max, float val) { // AU parameters are normalized in the range [0;1] | |||
val = min + val * (max - min); | |||
return (val < min) ? min : (val > max) ? max : val; | |||
} | |||
public: | |||
auUIObject(const char* label, FAUSTFLOAT* zone) : | |||
fLabel(label), fZone(zone) { | |||
} | |||
virtual ~auUIObject() { | |||
} | |||
virtual void GetName(char *text) { | |||
std::strcpy(text, fLabel.c_str()); | |||
} | |||
virtual void SetValue(double f) { | |||
*fZone = range(0.0f, 1.0f, (float) f); | |||
} | |||
virtual float GetValue() { | |||
return *fZone; | |||
} | |||
virtual void GetDisplay(char *text) { | |||
std::sprintf(text, "%f", *fZone); | |||
} | |||
virtual long GetID() { /* returns the sum of all the ASCII characters contained in the parameter's label */ | |||
int i; | |||
long acc; | |||
for (i = 0, acc = 0; i < fLabel.length(); i++) | |||
acc += (fLabel.c_str())[i]; | |||
return acc; | |||
} | |||
}; | |||
/**********************************************************************************/ | |||
class auToggleButton: public auUIObject { | |||
public: | |||
auToggleButton(const char* label, FAUSTFLOAT* zone) : | |||
auUIObject(label, zone) { | |||
} | |||
virtual ~auToggleButton() { | |||
} | |||
virtual float GetValue() { | |||
return *fZone; | |||
} | |||
virtual void SetValue(double f) { | |||
*fZone = (f > 0.5f) ? 1.0f : 0.0f; | |||
} | |||
virtual void GetDisplay(char *text) { | |||
(*fZone > 0.5f) ? std::strcpy(text, "ON") : std::strcpy(text, "OFF"); | |||
} | |||
}; | |||
/**********************************************************************************/ | |||
class auCheckButton: public auUIObject { | |||
public: | |||
auCheckButton(const char* label, FAUSTFLOAT* zone) : | |||
auUIObject(label, zone) { | |||
} | |||
virtual ~auCheckButton() { | |||
} | |||
virtual float GetValue() { | |||
return *fZone; | |||
} | |||
virtual void SetValue(double f) { | |||
*fZone = (f > 0.5f) ? 1.0f : 0.0f; | |||
} | |||
virtual void GetDisplay(char *text) { | |||
(*fZone > 0.5f) ? std::strcpy(text, "ON") : std::strcpy(text, "OFF"); | |||
} | |||
}; | |||
/**********************************************************************************/ | |||
class auButton: public auUIObject { | |||
public: | |||
auButton(const char* label, FAUSTFLOAT* zone) : | |||
auUIObject(label, zone) { | |||
} | |||
virtual ~auButton() { | |||
} | |||
virtual float GetValue() { | |||
return *fZone; | |||
} | |||
virtual void SetValue(double f) { | |||
*fZone = (f > 0.5f) ? 1.0f : 0.0f; | |||
} | |||
virtual void GetDisplay(char *text) { | |||
(*fZone > 0.5f) ? std::strcpy(text, "ON") : std::strcpy(text, "OFF"); | |||
} | |||
}; | |||
/**********************************************************************************/ | |||
class auSlider: public auUIObject { | |||
public: | |||
float fInit; | |||
float fMin; | |||
float fMax; | |||
float fStep; | |||
bool fIsVertical; | |||
public: | |||
auSlider(const char* label, FAUSTFLOAT* zone, float init, float min, float max, | |||
float step, bool isVertical) : | |||
auUIObject(label, zone), fInit(init), fMin(min), fMax(max), fStep(step), fIsVertical(isVertical) { | |||
} | |||
virtual ~auSlider() { | |||
} | |||
virtual float GetValue() { | |||
return (*fZone - fMin) / (fMax - fMin); | |||
} // normalize | |||
virtual void SetValue(double f) { | |||
*fZone = range(fMin, fMax, (float) f); | |||
} // expand | |||
}; | |||
/**********************************************************************************/ | |||
class auBargraph: public auUIObject { | |||
public: | |||
float fInit; | |||
float fMin; | |||
float fMax; | |||
float fStep; | |||
bool fIsVertical; | |||
public: | |||
auBargraph(const char* label, FAUSTFLOAT* zone, float min, float max, bool isVertical) : | |||
auUIObject(label, zone), fMin(min), fMax(max), fIsVertical(isVertical){ | |||
} | |||
virtual ~auBargraph() { | |||
} | |||
virtual float GetValue() { | |||
return (*fZone - fMin) / (fMax - fMin); | |||
} // normalize | |||
virtual void SetValue(double f) { | |||
*fZone = range(fMin, fMax, (float) f); | |||
} // expand | |||
}; | |||
/**********************************************************************************/ | |||
class auBox: public auUIObject { | |||
public: | |||
vector<auUIObject*> children; | |||
bool isVertical; | |||
auBox* parent; | |||
public: | |||
auBox(const char* label, auBox* inParent, bool inIsVertical) : | |||
auUIObject(label, NULL), parent(inParent), isVertical(inIsVertical) { | |||
} | |||
void add(auUIObject* child) { | |||
children.push_back(child); | |||
} | |||
virtual ~auBox() { | |||
} | |||
}; | |||
/**********************************************************************************/ | |||
//eunum Direction {HORIZONTAL, VERTICAL}; //TODO | |||
class auUI: public UI { | |||
public: | |||
vector<auUIObject*> fUITable; | |||
std::set <float*>knobSet; | |||
auBox* currentBox = NULL; | |||
auBox* boundingBox = NULL; | |||
public: | |||
auUI() { | |||
currentBox = boundingBox = new auBox("", NULL, true); | |||
} | |||
virtual ~auUI() { | |||
for (vector<auUIObject*>::iterator iter = fUITable.begin(); | |||
iter != fUITable.end(); iter++) | |||
delete *iter; | |||
//TODO delete boxes | |||
} | |||
virtual void declare(float* zone, const char* key, const char* value) | |||
{ | |||
if (zone == 0) | |||
{ | |||
if (strcmp(key, "hidden") == 0) | |||
{ | |||
} | |||
} | |||
else | |||
{ | |||
if (strcmp(key,"size") == 0) | |||
{ | |||
} | |||
else if (strcmp(key,"tooltip") == 0) | |||
{ | |||
} | |||
else if (strcmp(key,"unit") == 0) | |||
{ | |||
} | |||
if (strcmp(key,"hidden") == 0) | |||
{ | |||
} | |||
else if (strcmp(key,"style") == 0) | |||
{ | |||
if (strstr(value, "knob")) //TODO | |||
{ | |||
knobSet.insert(zone); | |||
} | |||
} | |||
else if (strcmp(key,"color") == 0) | |||
{ | |||
} | |||
else if (strcmp(key,"accx") == 0 | |||
|| strcmp(key,"accy") == 0 | |||
|| strcmp(key,"accz") == 0 | |||
|| strcmp(key,"gyrox") == 0 | |||
|| strcmp(key,"gyroy") == 0 | |||
|| strcmp(key,"gyroz") == 0 | |||
|| strcmp(key,"compass") == 0) | |||
{ | |||
} | |||
} | |||
} | |||
void addButton(const char* label, FAUSTFLOAT* zone) { | |||
auButton* button = new auButton(label, zone); | |||
fUITable.push_back(button); | |||
currentBox->add(button); | |||
} | |||
void openTabBox(const char* label) { | |||
} | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone) { | |||
auCheckButton* checkButton= new auCheckButton(label, zone); | |||
fUITable.push_back(checkButton); | |||
currentBox->add(checkButton); | |||
} | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, float init, float min, | |||
float max, float step) { | |||
auSlider* slider = new auSlider(label, zone, init, min, max, step, true); | |||
fUITable.push_back(slider); | |||
currentBox->add(slider); | |||
} | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, float init, float min, | |||
float max, float step) { | |||
auSlider* slider = new auSlider(label, zone, init, min, max, step, false); | |||
fUITable.push_back(slider); | |||
currentBox->add(slider); | |||
} | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, float init, float min, float max, | |||
float step) { | |||
auSlider* slider = new auSlider(label, zone, init, min, max, step, false); | |||
fUITable.push_back(slider); | |||
currentBox->add(slider); | |||
} | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, float min, float max) { | |||
auBargraph* bargraph = new auBargraph(label, zone, min, max, false); | |||
fUITable.push_back(bargraph); | |||
currentBox->add(bargraph); | |||
} | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, float min, float max) { | |||
auBargraph* bargraph = new auBargraph(label, zone, min, max, true); | |||
fUITable.push_back(bargraph); | |||
currentBox->add(bargraph); | |||
} | |||
void openHorizontalBox(const char* label) { | |||
auBox* box = new auBox(label, currentBox, false); | |||
currentBox->add(box); | |||
currentBox = box; | |||
} | |||
void openVerticalBox(const char* label) { | |||
auBox* box = new auBox(label, currentBox, true); | |||
currentBox->add(box); | |||
currentBox = box; | |||
} | |||
void closeBox() { | |||
if (currentBox) //TODO else? | |||
currentBox = currentBox->parent; | |||
} | |||
void SetValue(int index, double f) { | |||
assert(index < fUITable.size()); | |||
fUITable[index]->SetValue(f); | |||
} | |||
float GetValue(long index) { | |||
assert(index < fUITable.size()); | |||
return fUITable[index]->GetValue(); | |||
} | |||
void GetDisplay(long index, char *text) { | |||
assert(index < fUITable.size()); | |||
fUITable[index]->GetDisplay(text); | |||
} | |||
void GetName(long index, char *text) { | |||
assert(index < fUITable.size()); | |||
fUITable[index]->GetName(text); | |||
} | |||
long GetNumParams() { | |||
return fUITable.size(); | |||
} | |||
long makeID() | |||
/* Creates a (unique) id by summing all the parameter's labels, | |||
* then wrapping it in the range [0;maxNumberOfId] and adding | |||
* this number to the offset made by the Four Character ID: 'FAUS' | |||
*/ | |||
{ | |||
const long maxNumberOfId = 128; | |||
long baseid = 'FAUS'; | |||
long id = 0; | |||
for (int i = 0; i < fUITable.size(); i++) | |||
id += fUITable[i]->GetID(); | |||
return baseid + id % maxNumberOfId; | |||
} | |||
void addNumDisplay(char* label, float* zone, int precision) { | |||
} | |||
void addTextDisplay(char* label, float* zone, char* names[], float min, | |||
float max) { | |||
} | |||
}; | |||
@@ -0,0 +1,728 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __alsa_dsp__ | |||
#define __alsa_dsp__ | |||
#include <stdio.h> | |||
#include <pthread.h> | |||
#include <sys/types.h> | |||
#include <pwd.h> | |||
#include <limits.h> | |||
#include <alsa/asoundlib.h> | |||
#include "faust/audio/audio.h" | |||
#include "faust/dsp/dsp.h" | |||
/** | |||
DEFAULT ALSA PARAMETERS CONTROLLED BY ENVIRONMENT VARIABLES | |||
Some default parameters of Faust's ALSA applications are controlled by the following environment variables : | |||
FAUST2ALSA_DEVICE = "hw:0" | |||
FAUST2ALSA_FREQUENCY= 44100 | |||
FAUST2ALSA_BUFFER = 512 | |||
FAUST2ALSA_PERIODS = 2 | |||
*/ | |||
// handle 32/64 bits int size issues | |||
#ifdef __x86_64__ | |||
#define uint32 unsigned int | |||
#define uint64 unsigned long int | |||
#define int32 int | |||
#define int64 long int | |||
#else | |||
#define uint32 unsigned int | |||
#define uint64 unsigned long long int | |||
#define int32 int | |||
#define int64 long long int | |||
#endif | |||
// check 32/64 bits issues are correctly handled | |||
#define check_error(err) if (err) { printf("%s:%d, alsa error %d : %s\n", __FILE__, __LINE__, err, snd_strerror(err)); exit(1); } | |||
#define check_error_msg(err,msg) if (err) { fprintf(stderr, "%s:%d, %s : %s(%d)\n", __FILE__, __LINE__, msg, snd_strerror(err), err); exit(1); } | |||
#define display_error_msg(err,msg) if (err) { fprintf(stderr, "%s:%d, %s : %s(%d)\n", __FILE__, __LINE__, msg, snd_strerror(err), err); } | |||
/** | |||
* Used to set the priority and scheduling of the audi#include <sys/types.h> | |||
#include <pwd.h> | |||
o thread | |||
*/ | |||
static bool setRealtimePriority () | |||
{ | |||
struct passwd * pw; | |||
int err; | |||
uid_t uid; | |||
struct sched_param param; | |||
uid = getuid (); | |||
pw = getpwnam ("root"); | |||
err = setuid (pw->pw_uid); | |||
if (err==0) { | |||
param.sched_priority = 50; /* 0 to 99 */ | |||
err = sched_setscheduler(0, SCHED_RR, ¶m); | |||
err = setuid (uid); | |||
} | |||
return (err != -1); | |||
} | |||
/****************************************************************************** | |||
******************************************************************************* | |||
AUDIO INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
enum { kRead = 1, kWrite = 2, kReadWrite = 3 }; | |||
/** | |||
* A convenient class to pass parameters to AudioInterface | |||
*/ | |||
struct AudioParam | |||
{ | |||
const char* fCardName; | |||
unsigned int fFrequency; | |||
unsigned int fBuffering; | |||
unsigned int fPeriods; | |||
unsigned int fSoftInputs; | |||
unsigned int fSoftOutputs; | |||
AudioParam() : | |||
fCardName("hw:0"), | |||
fFrequency(44100), | |||
fBuffering(512), | |||
fPeriods(2), | |||
fSoftInputs(2), | |||
fSoftOutputs(2) | |||
{} | |||
AudioParam& cardName(const char* n) { fCardName = n; return *this; } | |||
AudioParam& frequency(int f) { fFrequency = f; return *this; } | |||
AudioParam& buffering(int fpb) { fBuffering = fpb; return *this; } | |||
AudioParam& periods(int p) { fPeriods = p; return *this; } | |||
AudioParam& inputs(int n) { fSoftInputs = n; return *this; } | |||
AudioParam& outputs(int n) { fSoftOutputs = n; return *this; } | |||
}; | |||
/** | |||
* An ALSA audio interface | |||
*/ | |||
struct AudioInterface : public AudioParam | |||
{ | |||
snd_pcm_t* fOutputDevice; | |||
snd_pcm_t* fInputDevice; | |||
snd_pcm_hw_params_t* fInputParams; | |||
snd_pcm_hw_params_t* fOutputParams; | |||
snd_pcm_format_t fSampleFormat; | |||
snd_pcm_access_t fSampleAccess; | |||
unsigned int fCardInputs; | |||
unsigned int fCardOutputs; | |||
unsigned int fChanInputs; | |||
unsigned int fChanOutputs; | |||
bool fDuplexMode; | |||
// interleaved mode audiocard buffers | |||
void* fInputCardBuffer; | |||
void* fOutputCardBuffer; | |||
// non interleaved mode audiocard buffers | |||
void* fInputCardChannels[256]; | |||
void* fOutputCardChannels[256]; | |||
// non interleaved mod, floating point software buffers | |||
float* fInputSoftChannels[256]; | |||
float* fOutputSoftChannels[256]; | |||
const char* cardName() { return fCardName; } | |||
int frequency() { return fFrequency; } | |||
int buffering() { return fBuffering; } | |||
int periods() { return fPeriods; } | |||
float** inputSoftChannels() { return fInputSoftChannels; } | |||
float** outputSoftChannels() { return fOutputSoftChannels; } | |||
bool duplexMode() { return fDuplexMode; } | |||
AudioInterface(const AudioParam& ap = AudioParam()) : AudioParam(ap) | |||
{ | |||
fInputDevice = 0; | |||
fOutputDevice = 0; | |||
fInputParams = 0; | |||
fOutputParams = 0; | |||
} | |||
/** | |||
* Open the audio interface | |||
*/ | |||
void open() | |||
{ | |||
int err; | |||
// try to open output device, quit if fail to open output device | |||
err = snd_pcm_open( &fOutputDevice, fCardName, SND_PCM_STREAM_PLAYBACK, 0 ); check_error(err) | |||
// setup output device parameters | |||
err = snd_pcm_hw_params_malloc ( &fOutputParams ); check_error(err) | |||
setAudioParams(fOutputDevice, fOutputParams); | |||
fCardOutputs = fSoftOutputs; | |||
snd_pcm_hw_params_set_channels_near(fOutputDevice, fOutputParams, &fCardOutputs); | |||
err = snd_pcm_hw_params (fOutputDevice, fOutputParams ); check_error(err); | |||
// allocate alsa output buffers | |||
if (fSampleAccess == SND_PCM_ACCESS_RW_INTERLEAVED) { | |||
fOutputCardBuffer = calloc(interleavedBufferSize(fOutputParams), 1); | |||
} else { | |||
for (unsigned int i = 0; i < fCardOutputs; i++) { | |||
fOutputCardChannels[i] = calloc(noninterleavedBufferSize(fOutputParams), 1); | |||
} | |||
} | |||
// check for duplex mode (if we need and have an input device) | |||
if (fSoftInputs == 0) { | |||
fDuplexMode = false; | |||
fCardInputs = 0; | |||
} else { | |||
// try to open input device | |||
err = snd_pcm_open( &fInputDevice, fCardName, SND_PCM_STREAM_CAPTURE, 0 ); | |||
if (err == 0) { | |||
fDuplexMode = true; | |||
} else { | |||
printf("Warning : no input device"); | |||
fDuplexMode = false; | |||
fCardInputs = 0; | |||
} | |||
} | |||
if (fDuplexMode) { | |||
// we have and need an input device | |||
// set the number of physical inputs close to what we need | |||
err = snd_pcm_hw_params_malloc ( &fInputParams ); check_error(err); | |||
setAudioParams(fInputDevice, fInputParams); | |||
fCardInputs = fSoftInputs; | |||
snd_pcm_hw_params_set_channels_near(fInputDevice, fInputParams, &fCardInputs); | |||
err = snd_pcm_hw_params (fInputDevice, fInputParams ); check_error(err); | |||
// allocation of alsa buffers | |||
if (fSampleAccess == SND_PCM_ACCESS_RW_INTERLEAVED) { | |||
fInputCardBuffer = calloc(interleavedBufferSize(fInputParams), 1); | |||
} else { | |||
for (unsigned int i = 0; i < fCardInputs; i++) { | |||
fInputCardChannels[i] = calloc(noninterleavedBufferSize(fInputParams), 1); | |||
} | |||
} | |||
} | |||
printf("inputs : %u, outputs : %u\n", fCardInputs, fCardOutputs); | |||
// allocation of floating point buffers needed by the dsp code | |||
fChanInputs = max(fSoftInputs, fCardInputs); assert (fChanInputs < 256); | |||
fChanOutputs = max(fSoftOutputs, fCardOutputs); assert (fChanOutputs < 256); | |||
for (unsigned int i = 0; i < fChanInputs; i++) { | |||
fInputSoftChannels[i] = (float*) calloc (fBuffering, sizeof(float)); | |||
for (unsigned int j = 0; j < fBuffering; j++) { | |||
fInputSoftChannels[i][j] = 0.0; | |||
} | |||
} | |||
for (unsigned int i = 0; i < fChanOutputs; i++) { | |||
fOutputSoftChannels[i] = (float*) calloc (fBuffering, sizeof(float)); | |||
for (unsigned int j = 0; j < fBuffering; j++) { | |||
fOutputSoftChannels[i][j] = 0.0; | |||
} | |||
} | |||
} | |||
void setAudioParams(snd_pcm_t* stream, snd_pcm_hw_params_t* params) | |||
{ | |||
int err; | |||
// set params record with initial values | |||
err = snd_pcm_hw_params_any ( stream, params ); | |||
check_error_msg(err, "unable to init parameters") | |||
// set alsa access mode (and fSampleAccess field) either to non interleaved or interleaved | |||
err = snd_pcm_hw_params_set_access (stream, params, SND_PCM_ACCESS_RW_NONINTERLEAVED ); | |||
if (err) { | |||
err = snd_pcm_hw_params_set_access (stream, params, SND_PCM_ACCESS_RW_INTERLEAVED ); | |||
check_error_msg(err, "unable to set access mode neither to non-interleaved or to interleaved"); | |||
} | |||
snd_pcm_hw_params_get_access(params, &fSampleAccess); | |||
// search for 32-bits or 16-bits format | |||
err = snd_pcm_hw_params_set_format (stream, params, SND_PCM_FORMAT_S32); | |||
if (err) { | |||
err = snd_pcm_hw_params_set_format (stream, params, SND_PCM_FORMAT_S16); | |||
check_error_msg(err, "unable to set format to either 32-bits or 16-bits"); | |||
} | |||
snd_pcm_hw_params_get_format(params, &fSampleFormat); | |||
// set sample frequency | |||
snd_pcm_hw_params_set_rate_near (stream, params, &fFrequency, 0); | |||
// set period and period size (buffering) | |||
err = snd_pcm_hw_params_set_period_size (stream, params, fBuffering, 0); | |||
check_error_msg(err, "period size not available"); | |||
err = snd_pcm_hw_params_set_periods (stream, params, fPeriods, 0); | |||
check_error_msg(err, "number of periods not available"); | |||
} | |||
ssize_t interleavedBufferSize (snd_pcm_hw_params_t* params) | |||
{ | |||
_snd_pcm_format format; snd_pcm_hw_params_get_format(params, &format); | |||
snd_pcm_uframes_t psize; snd_pcm_hw_params_get_period_size(params, &psize, NULL); | |||
unsigned int channels; snd_pcm_hw_params_get_channels(params, &channels); | |||
ssize_t bsize = snd_pcm_format_size (format, psize * channels); | |||
return bsize; | |||
} | |||
ssize_t noninterleavedBufferSize (snd_pcm_hw_params_t* params) | |||
{ | |||
_snd_pcm_format format; snd_pcm_hw_params_get_format(params, &format); | |||
snd_pcm_uframes_t psize; snd_pcm_hw_params_get_period_size(params, &psize, NULL); | |||
ssize_t bsize = snd_pcm_format_size (format, psize); | |||
return bsize; | |||
} | |||
void close() | |||
{} | |||
/** | |||
* Read audio samples from the audio card. Convert samples to floats and take | |||
* care of interleaved buffers | |||
*/ | |||
void read() | |||
{ | |||
if (fSampleAccess == SND_PCM_ACCESS_RW_INTERLEAVED) { | |||
int count = snd_pcm_readi(fInputDevice, fInputCardBuffer, fBuffering); | |||
if (count < 0) { | |||
//display_error_msg(count, "reading samples"); | |||
snd_pcm_prepare(fInputDevice); | |||
//check_error_msg(err, "preparing input stream"); | |||
} | |||
if (fSampleFormat == SND_PCM_FORMAT_S16) { | |||
short* buffer16b = (short*)fInputCardBuffer; | |||
for (unsigned int s = 0; s < fBuffering; s++) { | |||
for (unsigned int c = 0; c < fCardInputs; c++) { | |||
fInputSoftChannels[c][s] = float(buffer16b[c + s*fCardInputs])*(1.0/float(SHRT_MAX)); | |||
} | |||
} | |||
} else if (fSampleFormat == SND_PCM_FORMAT_S32) { | |||
int32* buffer32b = (int32*)fInputCardBuffer; | |||
for (unsigned int s = 0; s < fBuffering; s++) { | |||
for (unsigned int c = 0; c < fCardInputs; c++) { | |||
fInputSoftChannels[c][s] = float(buffer32b[c + s*fCardInputs])*(1.0/float(INT_MAX)); | |||
} | |||
} | |||
} else { | |||
printf("unrecognized input sample format : %u\n", fSampleFormat); | |||
exit(1); | |||
} | |||
} else if (fSampleAccess == SND_PCM_ACCESS_RW_NONINTERLEAVED) { | |||
int count = snd_pcm_readn(fInputDevice, fInputCardChannels, fBuffering); | |||
if (count < 0) { | |||
//display_error_msg(count, "reading samples"); | |||
snd_pcm_prepare(fInputDevice); | |||
//check_error_msg(err, "preparing input stream"); | |||
} | |||
if (fSampleFormat == SND_PCM_FORMAT_S16) { | |||
for (unsigned int c = 0; c < fCardInputs; c++) { | |||
short* chan16b = (short*)fInputCardChannels[c]; | |||
for (unsigned int s = 0; s < fBuffering; s++) { | |||
fInputSoftChannels[c][s] = float(chan16b[s])*(1.0/float(SHRT_MAX)); | |||
} | |||
} | |||
} else if (fSampleFormat == SND_PCM_FORMAT_S32) { | |||
for (unsigned int c = 0; c < fCardInputs; c++) { | |||
int32* chan32b = (int32*)fInputCardChannels[c]; | |||
for (unsigned int s = 0; s < fBuffering; s++) { | |||
fInputSoftChannels[c][s] = float(chan32b[s])*(1.0/float(INT_MAX)); | |||
} | |||
} | |||
} else { | |||
printf("unrecognized input sample format : %u\n", fSampleFormat); | |||
exit(1); | |||
} | |||
} else { | |||
check_error_msg(-10000, "unknown access mode"); | |||
} | |||
} | |||
/** | |||
* write the output soft channels to the audio card. Convert sample | |||
* format and interleaves buffers when needed | |||
*/ | |||
void write() | |||
{ | |||
recovery : | |||
if (fSampleAccess == SND_PCM_ACCESS_RW_INTERLEAVED) { | |||
if (fSampleFormat == SND_PCM_FORMAT_S16) { | |||
short* buffer16b = (short*)fOutputCardBuffer; | |||
for (unsigned int f = 0; f < fBuffering; f++) { | |||
for (unsigned int c = 0; c < fCardOutputs; c++) { | |||
float x = fOutputSoftChannels[c][f]; | |||
buffer16b[c + f*fCardOutputs] = short( max(min(x,1.0f),-1.0f) * float(SHRT_MAX) ) ; | |||
} | |||
} | |||
} else if (fSampleFormat == SND_PCM_FORMAT_S32) { | |||
int32* buffer32b = (int32*)fOutputCardBuffer; | |||
for (unsigned int f = 0; f < fBuffering; f++) { | |||
for (unsigned int c = 0; c < fCardOutputs; c++) { | |||
float x = fOutputSoftChannels[c][f]; | |||
buffer32b[c + f*fCardOutputs] = int( max(min(x,1.0f),-1.0f) * float(INT_MAX) ) ; | |||
} | |||
} | |||
} else { | |||
printf("unrecognized output sample format : %u\n", fSampleFormat); | |||
exit(1); | |||
} | |||
int count = snd_pcm_writei(fOutputDevice, fOutputCardBuffer, fBuffering); | |||
if (count<0) { | |||
//display_error_msg(count, "w3"); | |||
snd_pcm_prepare(fOutputDevice); | |||
//check_error_msg(err, "preparing output stream"); | |||
goto recovery; | |||
} | |||
} else if (fSampleAccess == SND_PCM_ACCESS_RW_NONINTERLEAVED) { | |||
if (fSampleFormat == SND_PCM_FORMAT_S16) { | |||
for (unsigned int c = 0; c < fCardOutputs; c++) { | |||
short* chan16b = (short*) fOutputCardChannels[c]; | |||
for (unsigned int f = 0; f < fBuffering; f++) { | |||
float x = fOutputSoftChannels[c][f]; | |||
chan16b[f] = short( max(min(x,1.0f),-1.0f) * float(SHRT_MAX) ) ; | |||
} | |||
} | |||
} else if (fSampleFormat == SND_PCM_FORMAT_S32) { | |||
for (unsigned int c = 0; c < fCardOutputs; c++) { | |||
int32* chan32b = (int32*) fOutputCardChannels[c]; | |||
for (unsigned int f = 0; f < fBuffering; f++) { | |||
float x = fOutputSoftChannels[c][f]; | |||
chan32b[f] = int( max(min(x,1.0f),-1.0f) * float(INT_MAX) ) ; | |||
} | |||
} | |||
} else { | |||
printf("unrecognized output sample format : %u\n", fSampleFormat); | |||
exit(1); | |||
} | |||
int count = snd_pcm_writen(fOutputDevice, fOutputCardChannels, fBuffering); | |||
if (count<0) { | |||
//display_error_msg(count, "w3"); | |||
snd_pcm_prepare(fOutputDevice); | |||
//check_error_msg(err, "preparing output stream"); | |||
goto recovery; | |||
} | |||
} else { | |||
check_error_msg(-10000, "unknown access mode"); | |||
} | |||
} | |||
/** | |||
* print short information on the audio device | |||
*/ | |||
void shortinfo() | |||
{ | |||
int err; | |||
snd_ctl_card_info_t* card_info; | |||
snd_ctl_t* ctl_handle; | |||
err = snd_ctl_open (&ctl_handle, fCardName, 0); check_error(err); | |||
snd_ctl_card_info_alloca (&card_info); | |||
err = snd_ctl_card_info(ctl_handle, card_info); check_error(err); | |||
printf("%s|%d|%d|%d|%d|%s\n", | |||
snd_ctl_card_info_get_driver(card_info), | |||
fCardInputs, fCardOutputs, | |||
fFrequency, fBuffering, | |||
snd_pcm_format_name((_snd_pcm_format)fSampleFormat)); | |||
} | |||
/** | |||
* print more detailled information on the audio device | |||
*/ | |||
void longinfo() | |||
{ | |||
int err; | |||
snd_ctl_card_info_t* card_info; | |||
snd_ctl_t* ctl_handle; | |||
printf("Audio Interface Description :\n"); | |||
printf("Sampling Frequency : %d, Sample Format : %s, buffering : %d\n", | |||
fFrequency, snd_pcm_format_name((_snd_pcm_format)fSampleFormat), fBuffering); | |||
printf("Software inputs : %2d, Software outputs : %2d\n", fSoftInputs, fSoftOutputs); | |||
printf("Hardware inputs : %2d, Hardware outputs : %2d\n", fCardInputs, fCardOutputs); | |||
printf("Channel inputs : %2d, Channel outputs : %2d\n", fChanInputs, fChanOutputs); | |||
// affichage des infos de la carte | |||
err = snd_ctl_open (&ctl_handle, fCardName, 0); check_error(err); | |||
snd_ctl_card_info_alloca (&card_info); | |||
err = snd_ctl_card_info(ctl_handle, card_info); check_error(err); | |||
printCardInfo(card_info); | |||
// affichage des infos liees aux streams d'entree-sortie | |||
if (fSoftInputs > 0) printHWParams(fInputParams); | |||
if (fSoftOutputs > 0) printHWParams(fOutputParams); | |||
} | |||
void printCardInfo(snd_ctl_card_info_t* ci) | |||
{ | |||
printf("Card info (address : %p)\n", ci); | |||
printf("\tID = %s\n", snd_ctl_card_info_get_id(ci)); | |||
printf("\tDriver = %s\n", snd_ctl_card_info_get_driver(ci)); | |||
printf("\tName = %s\n", snd_ctl_card_info_get_name(ci)); | |||
printf("\tLongName = %s\n", snd_ctl_card_info_get_longname(ci)); | |||
printf("\tMixerName = %s\n", snd_ctl_card_info_get_mixername(ci)); | |||
printf("\tComponents = %s\n", snd_ctl_card_info_get_components(ci)); | |||
printf("--------------\n"); | |||
} | |||
void printHWParams( snd_pcm_hw_params_t* params ) | |||
{ | |||
printf("HW Params info (address : %p)\n", params); | |||
#if 0 | |||
printf("\tChannels = %d\n", snd_pcm_hw_params_get_channels(params)); | |||
printf("\tFormat = %s\n", snd_pcm_format_name((_snd_pcm_format)snd_pcm_hw_params_get_format(params))); | |||
printf("\tAccess = %s\n", snd_pcm_access_name((_snd_pcm_access)snd_pcm_hw_params_get_access(params))); | |||
printf("\tRate = %d\n", snd_pcm_hw_params_get_rate(params, NULL)); | |||
printf("\tPeriods = %d\n", snd_pcm_hw_params_get_periods(params, NULL)); | |||
printf("\tPeriod size = %d\n", (int)snd_pcm_hw_params_get_period_size(params, NULL)); | |||
printf("\tPeriod time = %d\n", snd_pcm_hw_params_get_period_time(params, NULL)); | |||
printf("\tBuffer size = %d\n", (int)snd_pcm_hw_params_get_buffer_size(params)); | |||
printf("\tBuffer time = %d\n", snd_pcm_hw_params_get_buffer_time(params, NULL)); | |||
#endif | |||
printf("--------------\n"); | |||
} | |||
}; | |||
// lopt : Scan Command Line long int Arguments | |||
long lopt(int argc, char *argv[], const char* longname, const char* shortname, long def) | |||
{ | |||
for (int i=2; i<argc; i++) | |||
if ( strcmp(argv[i-1], shortname) == 0 || strcmp(argv[i-1], longname) == 0 ) | |||
return atoi(argv[i]); | |||
return def; | |||
} | |||
// sopt : Scan Command Line string Arguments | |||
const char* sopt(int argc, char *argv[], const char* longname, const char* shortname, const char* def) | |||
{ | |||
for (int i=2; i<argc; i++) | |||
if ( strcmp(argv[i-1], shortname) == 0 || strcmp(argv[i-1], longname) == 0 ) | |||
return argv[i]; | |||
return def; | |||
} | |||
// fopt : Scan Command Line flag option (without argument), return true if the flag | |||
bool fopt(int argc, char *argv[], const char* longname, const char* shortname) | |||
{ | |||
for (int i=1; i<argc; i++) | |||
if ( strcmp(argv[i], shortname) == 0 || strcmp(argv[i], longname) == 0 ) | |||
return true; | |||
return false; | |||
} | |||
/** | |||
* Return the value of an environment variable or defval if undefined. | |||
*/ | |||
static int getDefaultEnv(const char* name, int defval) | |||
{ | |||
const char* str = getenv(name); | |||
if (str) { | |||
return atoi(str); | |||
} else { | |||
return defval; | |||
} | |||
} | |||
/** | |||
* Return the value of an environment variable or defval if undefined. | |||
*/ | |||
static const char* getDefaultEnv(const char* name, const char* defval) | |||
{ | |||
const char* str = getenv(name); | |||
if (str) { | |||
return str; | |||
} else { | |||
return defval; | |||
} | |||
} | |||
/****************************************************************************** | |||
******************************************************************************* | |||
ALSA audio interface | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
static void* __run(void* ptr); | |||
class alsaaudio : public audio | |||
{ | |||
AudioInterface* fAudio; | |||
dsp* fDSP; | |||
pthread_t fAudioThread; | |||
bool fRunning; | |||
public: | |||
alsaaudio(int argc, char *argv[], dsp* DSP) : fDSP(DSP), fRunning(false) | |||
{ | |||
fAudio = new AudioInterface(AudioParam().cardName(sopt(argc, argv, "--device", "-d", getDefaultEnv("FAUST2ALSA_DEVICE", "hw:0"))) | |||
.frequency(lopt(argc, argv, "--frequency", "-f", getDefaultEnv("FAUST2ALSA_FREQUENCY", 44100))) | |||
.buffering(lopt(argc, argv, "--buffer", "-b", getDefaultEnv("FAUST2ALSA_BUFFER", 512))) | |||
.periods(lopt(argc, argv, "--periods", "-p", getDefaultEnv("FAUST2ALSA_PERIODS", 2))) | |||
.inputs(DSP->getNumInputs()) | |||
.outputs(DSP->getNumOutputs())); | |||
} | |||
alsaaudio(int srate, int bsize) : fDSP(0), fRunning(false) | |||
{ | |||
fAudio = new AudioInterface(AudioParam().cardName("hw:0") | |||
.frequency(srate) | |||
.buffering(bsize) | |||
.periods(2)); | |||
} | |||
virtual ~alsaaudio() { stop(); delete fAudio; } | |||
virtual bool init(const char */*name*/, dsp* DSP) | |||
{ | |||
fAudio->inputs(DSP->getNumInputs()); | |||
fAudio->outputs(DSP->getNumOutputs()); | |||
fAudio->open(); | |||
DSP->init(fAudio->frequency()); | |||
return true; | |||
} | |||
virtual bool start() | |||
{ | |||
fRunning = true; | |||
if (pthread_create(&fAudioThread, 0, __run, this)) { | |||
fRunning = false; | |||
} | |||
return fRunning; | |||
} | |||
virtual void stop() { | |||
if (fRunning) { | |||
fRunning = false; | |||
pthread_join(fAudioThread, 0); | |||
} | |||
} | |||
virtual int get_buffer_size() { return fAudio->buffering(); } | |||
virtual int get_sample_rate() { return fAudio->frequency(); } | |||
virtual void run() { | |||
bool rt = setRealtimePriority(); | |||
printf(rt ? "RT : ":"NRT: "); fAudio->shortinfo(); | |||
AVOIDDENORMALS; | |||
if (fAudio->duplexMode()) { | |||
fAudio->write(); | |||
fAudio->write(); | |||
while (fRunning) { | |||
fAudio->read(); | |||
fDSP->compute(fAudio->buffering(), fAudio->inputSoftChannels(), fAudio->outputSoftChannels()); | |||
fAudio->write(); | |||
} | |||
} else { | |||
fAudio->write(); | |||
while (fRunning) { | |||
fDSP->compute(fAudio->buffering(), fAudio->inputSoftChannels(), fAudio->outputSoftChannels()); | |||
fAudio->write(); | |||
} | |||
} | |||
} | |||
}; | |||
void* __run (void* ptr) | |||
{ | |||
alsaaudio * alsa = (alsaaudio*)ptr; | |||
alsa->run(); | |||
return 0; | |||
} | |||
#endif | |||
/********************END ARCHITECTURE SECTION (part 2/2)****************/ | |||
@@ -0,0 +1,479 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2015-2015 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
*************************************************************************/ | |||
#ifndef __android_dsp__ | |||
#define __android_dsp__ | |||
#include <android/log.h> | |||
#include <SLES/OpenSLES.h> | |||
#include <SLES/OpenSLES_Android.h> | |||
#include <time.h> | |||
#include "faust/audio/audio.h" | |||
#define CONV16BIT 32767.f | |||
#define CONVMYFLT (1.f/32767.f) | |||
#define NUM_INPUTS 2 | |||
#define NUM_OUTPUTS 2 | |||
#define CPU_TABLE_SIZE 16 | |||
struct CircularBuffer { | |||
short* fBuffer; | |||
int fReadIndex; | |||
int fWriteIndex; | |||
int fSize; | |||
int fChan; | |||
CircularBuffer(int frames, int chan) | |||
{ | |||
fBuffer = new short[frames * chan]; | |||
memset(fBuffer, 0, sizeof(short) * frames * chan); | |||
fSize = frames; | |||
fChan = chan; | |||
fReadIndex = 0; | |||
fWriteIndex = frames/2; // Set write index in the middle | |||
} | |||
~CircularBuffer() | |||
{ | |||
delete [] fBuffer; | |||
} | |||
short* getWritePtr() { return &fBuffer[fWriteIndex * fChan]; } | |||
short* getReadPtr() { return &fBuffer[fReadIndex * fChan]; } | |||
void moveWritePtr(int frames) | |||
{ | |||
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "moveWritePtr %x fWriteIndex = %ld", this, fWriteIndex); | |||
fWriteIndex = (fWriteIndex + frames) % fSize; | |||
} | |||
void moveReadPtr(int frames) | |||
{ | |||
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "moveReadPtr %x fReadIndex = %ld", this, fReadIndex); | |||
fReadIndex = (fReadIndex + frames) % fSize; | |||
} | |||
}; | |||
//http://stackoverflow.com/questions/17188761/how-to-obtain-computation-time-in-ndk | |||
class androidaudio : public audio { | |||
protected: | |||
dsp* fDsp; | |||
int fNumInChans; | |||
int fNumOutChans; | |||
unsigned int fSampleRate; | |||
unsigned int fBufferSize; | |||
int64_t fCPUTable[CPU_TABLE_SIZE]; | |||
int fCPUTableIndex; | |||
float** fInputs; | |||
float** fOutputs; | |||
CircularBuffer fOpenSLInputs; | |||
CircularBuffer fOpenSLOutputs; | |||
SLObjectItf fOpenSLEngine, fOutputMix, fInputBufferQueue, fOutputBufferQueue; | |||
SLAndroidSimpleBufferQueueItf fOutputBufferQueueInterface, fInputBufferQueueInterface; | |||
SLRecordItf fRecordInterface; | |||
SLPlayItf fPlayInterface; | |||
int64_t getTimeUsec() | |||
{ | |||
struct timespec now; | |||
clock_gettime(CLOCK_MONOTONIC, &now); | |||
return ((int64_t) now.tv_sec * 1000000000LL + now.tv_nsec)/1000; | |||
} | |||
void processAudio() | |||
{ | |||
int64_t t1 = getTimeUsec(); | |||
// Converting short input to float | |||
if (fNumInChans > 0) { | |||
short* input = fOpenSLInputs.getReadPtr(); | |||
for (int chan = 0; chan < NUM_INPUTS; chan++) { | |||
for (int i = 0; i < fBufferSize; i++) { | |||
fInputs[chan][i] = float(input[i * NUM_INPUTS + chan] * CONVMYFLT); | |||
} | |||
} | |||
fOpenSLInputs.moveReadPtr(fBufferSize); | |||
} | |||
// Compute DSP | |||
fDsp->compute(fBufferSize, fInputs, fOutputs); | |||
// Converting float to short output | |||
if (fNumOutChans > 0) { | |||
short* output = fOpenSLOutputs.getWritePtr(); | |||
for (int chan = 0; chan < NUM_OUTPUTS; chan++) { | |||
for (int i = 0; i < fBufferSize; i++) { | |||
output[i * NUM_OUTPUTS + chan] = short(min(1.f, max(-1.f, fOutputs[chan][i])) * CONV16BIT); | |||
} | |||
} | |||
fOpenSLOutputs.moveWritePtr(fBufferSize); | |||
} | |||
int64_t t2 = getTimeUsec(); | |||
fCPUTable[(fCPUTableIndex++)&(CPU_TABLE_SIZE-1)] = t2 - t1; | |||
} | |||
static void inputCallback(SLAndroidSimpleBufferQueueItf caller, void* arg) | |||
{ | |||
androidaudio* obj = (androidaudio*)arg; | |||
obj->inputCallback(caller); | |||
} | |||
void inputCallback(SLAndroidSimpleBufferQueueItf caller) | |||
{ | |||
SLresult result = (*caller)->Enqueue(caller, fOpenSLInputs.getWritePtr(), fBufferSize * sizeof(short) * NUM_INPUTS); | |||
fOpenSLInputs.moveWritePtr(fBufferSize); | |||
if (result != SL_RESULT_SUCCESS) { | |||
__android_log_print(ANDROID_LOG_ERROR, "Faust", "inputCallback Enqueue error = %d", int(result)); | |||
} | |||
} | |||
static void outputCallback(SLAndroidSimpleBufferQueueItf caller, void* arg) | |||
{ | |||
androidaudio* obj = (androidaudio*)arg; | |||
obj->outputCallback(caller); | |||
} | |||
void outputCallback(SLAndroidSimpleBufferQueueItf caller) | |||
{ | |||
// Output callback drives DSP computation | |||
processAudio(); | |||
SLresult result = (*caller)->Enqueue(caller, fOpenSLOutputs.getReadPtr(), fBufferSize * sizeof(short) * NUM_OUTPUTS); | |||
fOpenSLOutputs.moveReadPtr(fBufferSize); | |||
if (result != SL_RESULT_SUCCESS) { | |||
__android_log_print(ANDROID_LOG_ERROR, "Faust", "outputCallback Enqueue error = %d", int(result)); | |||
} | |||
} | |||
public: | |||
androidaudio(long srate, long bsize) | |||
: fDsp(0), fSampleRate(srate), | |||
fBufferSize(bsize), fCPUTableIndex(0), fNumInChans(0), fNumOutChans(0), | |||
fOpenSLEngine(0), fOutputMix(0), fInputBufferQueue(0), fOutputBufferQueue(0), | |||
fOpenSLInputs(bsize * 4, NUM_INPUTS), fOpenSLOutputs(bsize * 4, NUM_OUTPUTS) | |||
{ | |||
__android_log_print(ANDROID_LOG_ERROR, "Faust", "Constructor"); | |||
// Allocating memory for input channels. | |||
fInputs = new float*[NUM_INPUTS]; | |||
for (int i = 0; i < NUM_INPUTS; i++) { | |||
fInputs[i] = new float[fBufferSize]; | |||
memset(fInputs[i], 0, fBufferSize * sizeof(float)); | |||
} | |||
// Allocating memory for output channels. | |||
fOutputs = new float*[NUM_OUTPUTS]; | |||
for (int i = 0; i < NUM_OUTPUTS; i++) { | |||
fOutputs[i] = new float[fBufferSize]; | |||
memset(fOutputs[i], 0, fBufferSize * sizeof(float)); | |||
} | |||
} | |||
virtual ~androidaudio() | |||
{ | |||
__android_log_print(ANDROID_LOG_ERROR, "Faust", "Destructor"); | |||
if (fInputBufferQueue) { | |||
(*fInputBufferQueue)->Destroy(fInputBufferQueue); | |||
fInputBufferQueue = NULL; | |||
} | |||
if (fOutputBufferQueue) { | |||
(*fOutputBufferQueue)->Destroy(fOutputBufferQueue); | |||
fOutputBufferQueue = NULL; | |||
} | |||
if (fOutputMix) { | |||
(*fOutputMix)->Destroy(fOutputMix); | |||
fOutputMix = NULL; | |||
} | |||
if (fOpenSLEngine) { | |||
(*fOpenSLEngine)->Destroy(fOpenSLEngine); | |||
fOpenSLEngine = NULL; | |||
} | |||
for (int i = 0; i < NUM_INPUTS; i++) { | |||
delete [] fInputs[i]; | |||
} | |||
delete [] fInputs; | |||
for (int i = 0; i < NUM_OUTPUTS; i++) { | |||
delete [] fOutputs[i]; | |||
} | |||
delete [] fOutputs; | |||
} | |||
// DSP CPU load in percentage of the buffer size duration | |||
float getCPULoad() | |||
{ | |||
float sum = 0.f; | |||
for (int i = 0; i < CPU_TABLE_SIZE; i++) { | |||
sum += fCPUTable[i]; | |||
} | |||
return (sum/float(CPU_TABLE_SIZE))/(10000.f*float(fBufferSize)/float(fSampleRate)); | |||
} | |||
virtual bool init(const char* name, dsp* DSP) | |||
{ | |||
__android_log_print(ANDROID_LOG_ERROR, "Faust", "init"); | |||
fDsp = DSP; | |||
fNumInChans = fDsp->getNumInputs(); | |||
fNumOutChans = fDsp->getNumOutputs(); | |||
fDsp->init(fSampleRate); | |||
static const SLboolean requireds[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; | |||
SLresult result; | |||
SLuint32 sr; | |||
switch (fSampleRate) { | |||
case 8000: | |||
sr = SL_SAMPLINGRATE_8; | |||
break; | |||
case 11025: | |||
sr = SL_SAMPLINGRATE_11_025; | |||
break; | |||
case 16000: | |||
sr = SL_SAMPLINGRATE_16; | |||
break; | |||
case 22050: | |||
sr = SL_SAMPLINGRATE_22_05; | |||
break; | |||
case 24000: | |||
sr = SL_SAMPLINGRATE_24; | |||
break; | |||
case 32000: | |||
sr = SL_SAMPLINGRATE_32; | |||
break; | |||
case 44100: | |||
sr = SL_SAMPLINGRATE_44_1; | |||
break; | |||
case 48000: | |||
sr = SL_SAMPLINGRATE_48; | |||
break; | |||
case 64000: | |||
sr = SL_SAMPLINGRATE_64; | |||
break; | |||
case 88200: | |||
sr = SL_SAMPLINGRATE_88_2; | |||
break; | |||
case 96000: | |||
sr = SL_SAMPLINGRATE_96; | |||
break; | |||
case 192000: | |||
sr = SL_SAMPLINGRATE_192; | |||
break; | |||
default: | |||
return false; | |||
} | |||
// Create the OpenSL ES engine. | |||
result = slCreateEngine(&fOpenSLEngine, 0, NULL, 0, NULL, NULL); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fOpenSLEngine)->Realize(fOpenSLEngine, SL_BOOLEAN_FALSE); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
SLEngineItf openSLEngineInterface = NULL; | |||
result = (*fOpenSLEngine)->GetInterface(fOpenSLEngine, SL_IID_ENGINE, &openSLEngineInterface); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
// Create the output mix. | |||
result = (*openSLEngineInterface)->CreateOutputMix(openSLEngineInterface, &fOutputMix, 0, NULL, NULL); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fOutputMix)->Realize(fOutputMix, SL_BOOLEAN_FALSE); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
SLDataLocator_OutputMix outputMixLocator = { SL_DATALOCATOR_OUTPUTMIX, fOutputMix }; | |||
if (fNumInChans > 0) { | |||
// Create the input buffer queue. | |||
SLDataLocator_IODevice deviceInputLocator = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; | |||
SLDataSource inputSource = { &deviceInputLocator, NULL }; | |||
SLDataLocator_AndroidSimpleBufferQueue inputLocator = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 }; | |||
SLDataFormat_PCM inputFormat = { SL_DATAFORMAT_PCM, 2, sr, SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN }; | |||
SLDataSink inputSink = { &inputLocator, &inputFormat }; | |||
const SLInterfaceID inputInterfaces[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; | |||
result = (*openSLEngineInterface)->CreateAudioRecorder(openSLEngineInterface, &fInputBufferQueue, &inputSource, &inputSink, 2, inputInterfaces, requireds); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
#if DISABLE_AGC | |||
SLAndroidConfigurationItf configObject; | |||
result = (*fInputBufferQueue)->GetInterface(fInputBufferQueue, SL_IID_ANDROIDCONFIGURATION, &configObject); | |||
if (result == SL_RESULT_SUCCESS) { | |||
//SLuint32 mode = SL_ANDROID_RECORDING_PRESET_GENERIC; | |||
SLuint32 mode = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; | |||
result = (*configObject)->SetConfiguration(configObject, SL_ANDROID_KEY_RECORDING_PRESET, &mode, sizeof(mode)); | |||
if (result != SL_RESULT_SUCCESS) { | |||
__android_log_print(ANDROID_LOG_ERROR, "Faust", "SetConfiguration SL_ANDROID_KEY_RECORDING_PRESET error %d", result); | |||
} | |||
} else { | |||
__android_log_print(ANDROID_LOG_ERROR, "Faust", "GetInterface SL_IID_ANDROIDCONFIGURATION error %d", result); | |||
} | |||
#endif | |||
result = (*fInputBufferQueue)->Realize(fInputBufferQueue, SL_BOOLEAN_FALSE); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
} | |||
if (fNumOutChans > 0) { | |||
// Create the output buffer queue. | |||
SLDataLocator_AndroidSimpleBufferQueue outputLocator = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 }; | |||
SLDataFormat_PCM outputFormat = { SL_DATAFORMAT_PCM, 2, sr, SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN }; | |||
SLDataSource outputSource = { &outputLocator, &outputFormat }; | |||
const SLInterfaceID outputInterfaces[1] = { SL_IID_BUFFERQUEUE }; | |||
SLDataSink outputSink = { &outputMixLocator, NULL }; | |||
result = (*openSLEngineInterface)->CreateAudioPlayer(openSLEngineInterface, &fOutputBufferQueue, &outputSource, &outputSink, 1, outputInterfaces, requireds); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fOutputBufferQueue)->Realize(fOutputBufferQueue, SL_BOOLEAN_FALSE); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
} | |||
if (fNumInChans > 0) { // Initialize | |||
result = (*fInputBufferQueue)->GetInterface(fInputBufferQueue, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &fInputBufferQueueInterface); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fInputBufferQueueInterface)->RegisterCallback(fInputBufferQueueInterface, inputCallback, this); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fInputBufferQueue)->GetInterface(fInputBufferQueue, SL_IID_RECORD, &fRecordInterface); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fInputBufferQueueInterface)->Enqueue(fInputBufferQueueInterface, | |||
fOpenSLInputs.getWritePtr(), | |||
fBufferSize * sizeof(short) * NUM_INPUTS); | |||
fOpenSLInputs.moveWritePtr(fBufferSize); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fRecordInterface)->SetRecordState(fRecordInterface, SL_RECORDSTATE_STOPPED); | |||
if (result != SL_RESULT_SUCCESS) __android_log_print(ANDROID_LOG_ERROR, "Faust", "stop: SetRecordState error"); | |||
} | |||
if (fNumOutChans > 0) { // Initialize | |||
result = (*fOutputBufferQueue)->GetInterface(fOutputBufferQueue, SL_IID_BUFFERQUEUE, &fOutputBufferQueueInterface); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fOutputBufferQueueInterface)->RegisterCallback(fOutputBufferQueueInterface, outputCallback, this); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fOutputBufferQueue)->GetInterface(fOutputBufferQueue, SL_IID_PLAY, &fPlayInterface); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fOutputBufferQueueInterface)->Enqueue(fOutputBufferQueueInterface, | |||
fOpenSLOutputs.getReadPtr(), | |||
fBufferSize * sizeof(short) * NUM_OUTPUTS); | |||
fOpenSLOutputs.moveReadPtr(fBufferSize); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
result = (*fPlayInterface)->SetPlayState(fPlayInterface, SL_PLAYSTATE_STOPPED); | |||
if (result != SL_RESULT_SUCCESS) __android_log_print(ANDROID_LOG_ERROR, "Faust", "stop: SetPlayState error"); | |||
} | |||
return true; | |||
} | |||
virtual bool start() | |||
{ | |||
__android_log_print(ANDROID_LOG_ERROR, "Faust", "start"); | |||
SLresult result; | |||
if (fNumInChans > 0) { | |||
// start the inout buffer queue. | |||
result = (*fRecordInterface)->SetRecordState(fRecordInterface, SL_RECORDSTATE_RECORDING); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
} | |||
if (fNumOutChans > 0) { | |||
// start the output buffer queue. | |||
result = (*fPlayInterface)->SetPlayState(fPlayInterface, SL_PLAYSTATE_PLAYING); | |||
if (result != SL_RESULT_SUCCESS) return false; | |||
} | |||
return true; | |||
} | |||
virtual void stop() | |||
{ | |||
__android_log_print(ANDROID_LOG_ERROR, "Faust", "stop"); | |||
SLresult result; | |||
if (fNumInChans > 0) { | |||
result = (*fRecordInterface)->SetRecordState(fRecordInterface, SL_RECORDSTATE_PAUSED); | |||
if (result != SL_RESULT_SUCCESS) __android_log_print(ANDROID_LOG_ERROR, "Faust", "stop: SetRecordState error"); | |||
} | |||
if (fNumOutChans > 0) { | |||
result = (*fPlayInterface)->SetPlayState(fPlayInterface, SL_PLAYSTATE_PAUSED); | |||
if (result != SL_RESULT_SUCCESS) __android_log_print(ANDROID_LOG_ERROR, "Faust", "stop: SetPlayState error"); | |||
} | |||
} | |||
virtual int get_buffer_size() | |||
{ | |||
return fBufferSize; | |||
} | |||
virtual int get_sample_rate() | |||
{ | |||
return fSampleRate; | |||
} | |||
virtual int get_num_inputs() | |||
{ | |||
return fNumInChans; | |||
} | |||
virtual int get_num_outputs() | |||
{ | |||
return fNumOutChans; | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,71 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
/****************************************************************************** | |||
******************************************************************************* | |||
An abstraction layer over audio layer | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
#ifndef __audio__ | |||
#define __audio__ | |||
class dsp; | |||
typedef void (* shutdown_callback)(const char* message, void* arg); | |||
class audio { | |||
public: | |||
audio() {} | |||
virtual ~audio() {} | |||
virtual bool init(const char* name, dsp*) = 0; | |||
virtual bool start() = 0; | |||
virtual void stop() = 0; | |||
virtual void shutdown(shutdown_callback cb, void* arg) {} | |||
virtual int get_buffer_size() = 0; | |||
virtual int get_sample_rate() = 0; | |||
virtual int get_num_inputs() { return -1; } | |||
virtual int get_num_outputs() { return -1; } | |||
virtual float get_cpu_load() { return 0.f; } | |||
}; | |||
#endif |
@@ -0,0 +1,95 @@ | |||
/************************************************************************ | |||
************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __audio_channels__ | |||
#define __audio_channels__ | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
class channels | |||
{ | |||
private: | |||
int fNumFrames; | |||
int fNumChannels; | |||
FAUSTFLOAT** fBuffers; | |||
public: | |||
channels(int nframes, int nchannels) | |||
{ | |||
fBuffers = new FAUSTFLOAT*[nchannels]; | |||
fNumFrames = nframes; | |||
fNumChannels = nchannels; | |||
// allocate audio channels | |||
for (int i = 0; i < fNumChannels; i++) { | |||
fBuffers[i] = new FAUSTFLOAT[fNumFrames]; | |||
} | |||
} | |||
void zero() | |||
{ | |||
// allocate audio channels | |||
for (int i = 0; i < fNumChannels; i++) { | |||
for (int f = 0; f < fNumFrames; f++) { | |||
fBuffers[i][f] = FAUSTFLOAT(0.0); | |||
} | |||
} | |||
} | |||
void impulse() | |||
{ | |||
// allocate audio channels | |||
for (int i = 0; i < fNumChannels; i++) { | |||
fBuffers[i][0] = FAUSTFLOAT(1.0); | |||
for (int f = 1; f < fNumFrames; f++) { | |||
fBuffers[i][f] = FAUSTFLOAT(0.0); | |||
} | |||
} | |||
} | |||
void display() | |||
{ | |||
for (int i = 0; i < fNumChannels; i++) { | |||
for (int f = 0; f < fNumFrames; f++) { | |||
std::cout << "chan = " << i << " frame = " << f << " value = " << fBuffers[i][f] << std::endl; | |||
} | |||
} | |||
} | |||
virtual ~channels() | |||
{ | |||
// free separate input channels | |||
for (int i = 0; i < fNumChannels; i++) { | |||
delete [] fBuffers[i]; | |||
} | |||
delete [] fBuffers; | |||
} | |||
FAUSTFLOAT** buffers() { return fBuffers; } | |||
}; | |||
#endif | |||
@@ -0,0 +1,727 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __coreaudio_ios_dsp__ | |||
#define __coreaudio_ios_dsp__ | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <limits.h> | |||
#include <math.h> | |||
#include <errno.h> | |||
#include <time.h> | |||
#include "faust/audio/audio.h" | |||
#include "faust/dsp/dsp.h" | |||
#include <AudioToolbox/AudioConverter.h> | |||
#include <AudioToolbox/AudioServices.h> | |||
#include <AudioUnit/AudioUnit.h> | |||
using namespace std; | |||
/****************************************************************************** | |||
******************************************************************************* | |||
COREAUDIO INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
#define OPEN_ERR -1 | |||
#define NO_ERR 0 | |||
class TiPhoneCoreAudioRenderer | |||
{ | |||
protected: | |||
AudioUnit fAUHAL; | |||
int fDevNumInChans; | |||
int fDevNumOutChans; | |||
int fHWNumInChans; | |||
int fHWNumOutChans; | |||
dsp* fDSP; | |||
AudioBufferList* fCAInputData; | |||
static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) | |||
{ | |||
printf("- - - - - - - - - - - - - - - - - - - -\n"); | |||
printf(" Sample Rate:%f\n", inDesc->mSampleRate); | |||
printf(" Format ID:%.*s\n", (int) sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); | |||
printf(" Format Flags:%lX\n", inDesc->mFormatFlags); | |||
printf(" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket); | |||
printf(" Frames per Packet:%ld\n", inDesc->mFramesPerPacket); | |||
printf(" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame); | |||
printf(" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame); | |||
printf(" Bits per Channel:%ld\n", inDesc->mBitsPerChannel); | |||
printf("- - - - - - - - - - - - - - - - - - - -\n"); | |||
} | |||
static void printError(OSStatus err) | |||
{ | |||
switch (err) { | |||
case kAudioConverterErr_FormatNotSupported: | |||
printf("error code : kAudioConverterErr_FormatNotSupported\n"); | |||
break; | |||
case kAudioConverterErr_OperationNotSupported: | |||
printf("error code : kAudioConverterErr_OperationNotSupported\n"); | |||
break; | |||
case kAudioConverterErr_PropertyNotSupported: | |||
printf("error code : kAudioConverterErr_PropertyNotSupported\n"); | |||
break; | |||
case kAudioConverterErr_InvalidInputSize: | |||
printf("error code : kAudioConverterErr_InvalidInputSize\n"); | |||
break; | |||
case kAudioConverterErr_InvalidOutputSize: | |||
printf("error code : kAudioConverterErr_InvalidOutputSize\n"); | |||
break; | |||
case kAudioConverterErr_UnspecifiedError: | |||
printf("error code : kAudioConverterErr_UnspecifiedError\n"); | |||
break; | |||
case kAudioConverterErr_BadPropertySizeError: | |||
printf("error code : kAudioConverterErr_BadPropertySizeError\n"); | |||
break; | |||
case kAudioConverterErr_RequiresPacketDescriptionsError: | |||
printf("error code : kAudioConverterErr_RequiresPacketDescriptionsError\n"); | |||
break; | |||
case kAudioConverterErr_InputSampleRateOutOfRange: | |||
printf("error code : kAudioConverterErr_InputSampleRateOutOfRange\n"); | |||
break; | |||
case kAudioConverterErr_OutputSampleRateOutOfRange: | |||
printf("error code : kAudioConverterErr_OutputSampleRateOutOfRange\n"); | |||
break; | |||
default: | |||
printf("error code : unknown\n"); | |||
break; | |||
} | |||
} | |||
static OSStatus Render(void *inRefCon,AudioUnitRenderActionFlags *ioActionFlags, | |||
const AudioTimeStamp *inTimeStamp, | |||
UInt32, | |||
UInt32 inNumberFrames, | |||
AudioBufferList *ioData) | |||
{ | |||
return static_cast<TiPhoneCoreAudioRenderer*>(inRefCon)->Render(ioActionFlags, inTimeStamp, inNumberFrames, ioData); | |||
} | |||
OSStatus Render(AudioUnitRenderActionFlags *ioActionFlags, | |||
const AudioTimeStamp *inTimeStamp, | |||
UInt32 inNumberFrames, | |||
AudioBufferList *ioData) | |||
{ | |||
OSStatus err = noErr; | |||
if (fDevNumInChans > 0) { | |||
err = AudioUnitRender(fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, fCAInputData); | |||
} | |||
if (err == noErr) { | |||
float* fInChannel[fDevNumInChans]; | |||
float* fOutChannel[fDevNumOutChans]; | |||
for (int chan = 0; chan < fDevNumInChans; chan++) { | |||
fInChannel[chan] = (float*)fCAInputData->mBuffers[chan].mData; | |||
} | |||
for (int chan = 0; chan < fDevNumOutChans; chan++) { | |||
fOutChannel[chan] = (float*)ioData->mBuffers[chan].mData; | |||
} | |||
fDSP->compute((int)inNumberFrames, fInChannel, fOutChannel); | |||
} | |||
return err; | |||
} | |||
static void InterruptionListener(void *inClientData, UInt32 inInterruption) | |||
{ | |||
TiPhoneCoreAudioRenderer *obj = (TiPhoneCoreAudioRenderer*)inClientData; | |||
printf("Session interrupted! --- %s ---", (inInterruption == kAudioSessionBeginInterruption) ? "Begin Interruption" : "End Interruption"); | |||
if (inInterruption == kAudioSessionEndInterruption) { | |||
// Make sure we are again the active session | |||
AudioSessionSetActive(true); | |||
obj->SetupMixing(); | |||
AudioOutputUnitStart(obj->fAUHAL); | |||
} | |||
if (inInterruption == kAudioSessionBeginInterruption) { | |||
AudioOutputUnitStop(obj->fAUHAL); | |||
} | |||
} | |||
int SetupMixing() | |||
{ | |||
OSStatus err; | |||
/* | |||
01/07/2014 : cause iRig to fail, so deactivated for now... | |||
CFStringRef route; | |||
UInt32 routesize = sizeof(route); | |||
OSStatus err = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &routesize, &route); | |||
if (err == noErr) { | |||
if (CFStringCompare(route, CFSTR("ReceiverAndMicrophone"), 0) == kCFCompareEqualTo || CFStringCompare(route,CFSTR("Receiver"), 0) == kCFCompareEqualTo) { | |||
// Re-route audio to the speaker (not the receiver, which no music app will ever want) | |||
printf("Rerouting audio to speaker\n"); | |||
UInt32 newRoute = kAudioSessionOverrideAudioRoute_Speaker; | |||
AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(newRoute), &newRoute); | |||
} | |||
} | |||
*/ | |||
UInt32 allowMixing = true; | |||
err = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(allowMixing), &allowMixing); | |||
if (err != noErr) { | |||
printf("Could not set audio session mixing\n"); | |||
printError(err); | |||
return -1; | |||
} else { | |||
return 0; | |||
} | |||
} | |||
static void AudioSessionPropertyListener(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void* inData) | |||
{ | |||
TiPhoneCoreAudioRenderer *obj = (TiPhoneCoreAudioRenderer*)inData; | |||
switch (inID) { | |||
case kAudioSessionProperty_ServerDied: { | |||
printf("kAudioSessionProperty_ServerDied\n"); | |||
break; | |||
} | |||
case kAudioSessionProperty_AudioRouteChange: { | |||
printf("kAudioSessionProperty_AudioRouteChange\n"); | |||
obj->SetupMixing(); | |||
break; | |||
} | |||
case kAudioSessionProperty_AudioInputAvailable: { | |||
printf("kAudioSessionProperty_AudioInputAvailable\n"); | |||
obj->SetupMixing(); | |||
break; | |||
} | |||
} | |||
} | |||
static int SetAudioCategory(int input, int output) | |||
{ | |||
// Set the audioCategory the way Faust DSP wants | |||
UInt32 audioCategory; | |||
if ((input > 0) && (output > 0)) { | |||
audioCategory = kAudioSessionCategory_PlayAndRecord; | |||
printf("AudioCategory kAudioSessionCategory_PlayAndRecord\n"); | |||
} else if (input > 0) { | |||
audioCategory = kAudioSessionCategory_RecordAudio; | |||
printf("AudioCategory kAudioSessionCategory_RecordAudio\n"); | |||
} else if (output > 0) { | |||
audioCategory = kAudioSessionCategory_MediaPlayback; | |||
printf("AudioCategory kAudioSessionCategory_MediaPlayback\n"); | |||
} | |||
OSStatus err = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory); | |||
if (err != noErr) { | |||
printf("Couldn't set audio category\n"); | |||
printError(err); | |||
return OPEN_ERR; | |||
} | |||
// 09/07/2015 : https://developer.apple.com/library/ios/qa/qa1754/_index.html | |||
if (audioCategory == kAudioSessionCategory_PlayAndRecord) { | |||
UInt32 overrideAudioRoute = 1; | |||
err = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(UInt32), &overrideAudioRoute); | |||
if (err != noErr) { | |||
printf("Error setting kAudioSessionProperty_OverrideCategoryDefaultToSpeaker\n"); | |||
printError(err); | |||
} | |||
UInt32 allowBluetoothInput = 1; | |||
err = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, sizeof(UInt32), &allowBluetoothInput); | |||
if (err != noErr) { | |||
printf("Error setting kAudioSessionProperty_OverrideCategoryEnableBluetoothInput\n"); | |||
printError(err); | |||
} | |||
} | |||
#if NOAGC | |||
// If input is used, disable AGC | |||
if (audioCategory == kAudioSessionCategory_RecordAudio || audioCategory == kAudioSessionCategory_PlayAndRecord) { | |||
UInt32 sessionMode = kAudioSessionMode_Measurement; | |||
err = AudioSessionSetProperty(kAudioSessionProperty_Mode, sizeof(sessionMode), &sessionMode); | |||
if (err != noErr) { | |||
printf("Error setting kAudioSessionMode_Measurement\n"); | |||
printError(err); | |||
} | |||
UInt32 availableGain; | |||
UInt32 outSize = sizeof(availableGain); | |||
err = AudioSessionGetProperty(kAudioSessionProperty_InputGainAvailable, &outSize, &availableGain); | |||
if (err != noErr) { | |||
printf("Error getting kAudioSessionProperty_InputGainAvailable\n"); | |||
printError(err); | |||
} else { | |||
Float32 gain; | |||
printf("Getting kAudioSessionProperty_InputGainAvailable OK\n"); | |||
outSize = sizeof(Float32); | |||
AudioSessionGetProperty(kAudioSessionProperty_InputGainScalar, &outSize, &gain); | |||
printf("Getting kAudioSessionProperty_InputGainScalar : %f\n", gain); | |||
gain = 1.0f; | |||
err = AudioSessionSetProperty(kAudioSessionProperty_InputGainScalar, sizeof(Float32), &gain); | |||
if (err != noErr) { | |||
printf("Error setting kAudioSessionProperty_InputGainScalar\n"); | |||
printError(err); | |||
} else { | |||
printf("Setting kAudioSessionProperty_InputGainAvailable to 1.0 OK\n"); | |||
} | |||
} | |||
} | |||
#endif | |||
return NO_ERR; | |||
} | |||
int SetParameters(int bufferSize, int samplerate) | |||
{ | |||
OSStatus err; | |||
UInt32 outSize; | |||
UInt32 enableIO; | |||
AudioStreamBasicDescription srcFormat, dstFormat; | |||
printf("SetParameters fDevNumInChans = %d fDevNumOutChans = %d bufferSize = %d samplerate = %d\n", fDevNumInChans, fDevNumOutChans, bufferSize, samplerate); | |||
err = AudioSessionSetActive(true); | |||
if (err != noErr) { | |||
printf("Couldn't set audio session active\n"); | |||
printError(err); | |||
return OPEN_ERR; | |||
} | |||
AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, AudioSessionPropertyListener, this); | |||
AudioSessionAddPropertyListener(kAudioSessionProperty_AudioInputAvailable, AudioSessionPropertyListener, this); | |||
AudioSessionAddPropertyListener(kAudioSessionProperty_ServerDied, AudioSessionPropertyListener, this); | |||
if (SetAudioCategory(fDevNumInChans, fDevNumOutChans) != NO_ERR) { | |||
return OPEN_ERR; | |||
} | |||
// Scan Hardware | |||
outSize = sizeof(fHWNumInChans); | |||
err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, &outSize, &fHWNumInChans); | |||
if (err != noErr) { | |||
fHWNumInChans = 0; | |||
printf("Couldn't get hw input channels\n"); | |||
printError(err); | |||
} else { | |||
printf("Get hw input channels %d\n", fHWNumInChans); | |||
} | |||
outSize = sizeof(fHWNumOutChans); | |||
err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputNumberChannels, &outSize, &fHWNumOutChans); | |||
if (err != noErr) { | |||
fHWNumOutChans = 0; | |||
printf("Couldn't get hw output channels\n"); | |||
printError(err); | |||
} else { | |||
printf("Get hw output channels %d\n", fHWNumOutChans); | |||
} | |||
// Possibly reset the audioCategory the way hardware allows | |||
if (SetAudioCategory(fHWNumInChans, fHWNumOutChans) != NO_ERR) { | |||
return OPEN_ERR; | |||
} | |||
if (SetupMixing() < 0) { | |||
return OPEN_ERR; | |||
} | |||
Float64 hwSampleRate; | |||
outSize = sizeof(hwSampleRate); | |||
err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &outSize, &hwSampleRate); | |||
if (err != noErr) { | |||
printf("Couldn't get hw sample rate\n"); | |||
printError(err); | |||
return OPEN_ERR; | |||
} else { | |||
printf("Get hw sample rate %f\n", hwSampleRate); | |||
} | |||
Float32 hwBufferSize; | |||
outSize = sizeof(hwBufferSize); | |||
err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration, &outSize, &hwBufferSize); | |||
if (err != noErr) { | |||
printf("Couldn't get hw buffer duration\n"); | |||
printError(err); | |||
return OPEN_ERR; | |||
} else { | |||
printf("Get hw buffer duration %f\n", hwBufferSize); | |||
} | |||
Float32 preferredPeriodDuration = float(bufferSize) / float(samplerate); | |||
printf("preferredPeriodDuration %f \n", preferredPeriodDuration); | |||
err = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(preferredPeriodDuration), &preferredPeriodDuration); | |||
if (err != noErr) { | |||
printf("Couldn't set i/o buffer duration\n"); | |||
printError(err); | |||
return OPEN_ERR; | |||
} | |||
Float32 actualPeriodDuration; | |||
outSize = sizeof(actualPeriodDuration); | |||
err = AudioSessionGetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, &outSize, &actualPeriodDuration); | |||
if (err != noErr) { | |||
printf("Couldn't get hw buffer duration\n"); | |||
printError(err); | |||
return OPEN_ERR; | |||
} | |||
printf("preferredPeriodDuration %f actualPeriodDuration %f\n", preferredPeriodDuration, actualPeriodDuration); | |||
if (preferredPeriodDuration != actualPeriodDuration) { | |||
printf("Couldn't set hw buffer duration\n"); | |||
return OPEN_ERR; | |||
} | |||
Float64 preferredSamplerate = float(samplerate); | |||
err = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate, sizeof(preferredSamplerate), &preferredSamplerate); | |||
if (err != noErr) { | |||
printf("Couldn't set i/o sample rate\n"); | |||
printError(err); | |||
return OPEN_ERR; | |||
} | |||
Float32 inputLatency; | |||
outSize = sizeof(inputLatency); | |||
err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputLatency, &outSize, &inputLatency); | |||
if (err != noErr) { | |||
printf("Couldn't get inputLatency\n"); | |||
printError(err); | |||
} else { | |||
printf("inputLatency in sec : %f\n", inputLatency); | |||
} | |||
Float32 outputLatency; | |||
outSize = sizeof(outputLatency); | |||
err = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency, &outSize, &outputLatency); | |||
if (err != noErr) { | |||
printf("Couldn't get outputLatency\n"); | |||
printError(err); | |||
} else { | |||
printf("outputLatency in sec : %f\n", outputLatency); | |||
} | |||
// AUHAL | |||
AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple, 0, 0}; | |||
AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd); | |||
err = AudioComponentInstanceNew(HALOutput, &fAUHAL); | |||
if (err != noErr) { | |||
printf("Error calling OpenAComponent\n"); | |||
printError(err); | |||
goto error; | |||
} | |||
enableIO = 1; | |||
err = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output\n"); | |||
printError(err); | |||
goto error; | |||
} | |||
enableIO = 1; | |||
err = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input\n"); | |||
printError(err); | |||
goto error; | |||
} | |||
UInt32 maxFPS; | |||
outSize = sizeof(maxFPS); | |||
err = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFPS, &outSize); | |||
if (err != noErr) { | |||
printf("Couldn't get kAudioUnitProperty_MaximumFramesPerSlice\n"); | |||
printError(err); | |||
goto error; | |||
} else { | |||
printf("Get kAudioUnitProperty_MaximumFramesPerSlice %d\n", (unsigned int)maxFPS); | |||
} | |||
err = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&bufferSize, sizeof(UInt32)); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n"); | |||
printError(err); | |||
goto error; | |||
} | |||
err = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&bufferSize, sizeof(UInt32)); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n"); | |||
printError(err); | |||
goto error; | |||
} | |||
err = AudioUnitInitialize(fAUHAL); | |||
if (err != noErr) { | |||
printf("Cannot initialize AUHAL unit\n"); | |||
printError(err); | |||
goto error; | |||
} | |||
// Setting format | |||
if (fDevNumInChans > 0) { | |||
outSize = sizeof(AudioStreamBasicDescription); | |||
err = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, &outSize); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n"); | |||
printError(err); | |||
} | |||
PrintStreamDesc(&srcFormat); | |||
srcFormat.mFormatID = kAudioFormatLinearPCM; | |||
srcFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; | |||
srcFormat.mBytesPerPacket = sizeof(AudioUnitSampleType); | |||
srcFormat.mFramesPerPacket = 1; | |||
srcFormat.mBytesPerFrame = sizeof(AudioUnitSampleType); | |||
srcFormat.mChannelsPerFrame = fDevNumInChans; | |||
srcFormat.mBitsPerChannel = 32; | |||
PrintStreamDesc(&srcFormat); | |||
err = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n"); | |||
printError(err); | |||
} | |||
} | |||
if (fDevNumOutChans > 0) { | |||
outSize = sizeof(AudioStreamBasicDescription); | |||
err = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, &outSize); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input\n"); | |||
printError(err); | |||
} | |||
PrintStreamDesc(&dstFormat); | |||
dstFormat.mFormatID = kAudioFormatLinearPCM; | |||
dstFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved; | |||
dstFormat.mBytesPerPacket = sizeof(AudioUnitSampleType); | |||
dstFormat.mFramesPerPacket = 1; | |||
dstFormat.mBytesPerFrame = sizeof(AudioUnitSampleType); | |||
dstFormat.mChannelsPerFrame = fDevNumOutChans; | |||
dstFormat.mBitsPerChannel = 32; | |||
PrintStreamDesc(&dstFormat); | |||
err = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input\n"); | |||
printError(err); | |||
} | |||
} | |||
if (fDevNumInChans > 0 && fDevNumOutChans == 0) { | |||
AURenderCallbackStruct output; | |||
output.inputProc = Render; | |||
output.inputProcRefCon = this; | |||
err = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1\n"); | |||
printError(err); | |||
goto error; | |||
} | |||
} else { | |||
AURenderCallbackStruct output; | |||
output.inputProc = Render; | |||
output.inputProcRefCon = this; | |||
err = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); | |||
if (err != noErr) { | |||
printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0\n"); | |||
printError(err); | |||
goto error; | |||
} | |||
} | |||
// Possibly prepare input buffers | |||
if (fDevNumInChans > 0) { | |||
fCAInputData = (AudioBufferList*)malloc(sizeof(float) + fDevNumInChans * sizeof(AudioBuffer)); | |||
fCAInputData->mNumberBuffers = fDevNumInChans; | |||
for (int i = 0; i < fDevNumInChans; i++) { | |||
fCAInputData->mBuffers[i].mNumberChannels = 1; | |||
fCAInputData->mBuffers[i].mDataByteSize = bufferSize * sizeof(float); | |||
fCAInputData->mBuffers[i].mData = malloc(bufferSize * sizeof(float)); | |||
} | |||
} | |||
return NO_ERR; | |||
error: | |||
AudioUnitUninitialize(fAUHAL); | |||
AudioComponentInstanceDispose(fAUHAL); | |||
return OPEN_ERR; | |||
} | |||
public: | |||
TiPhoneCoreAudioRenderer() | |||
:fAUHAL(0), fDevNumInChans(0), fDevNumOutChans(0), | |||
fHWNumInChans(0), fHWNumOutChans(0), | |||
fDSP(0), fCAInputData(NULL) | |||
{} | |||
virtual ~TiPhoneCoreAudioRenderer() | |||
{ | |||
if (fCAInputData) { | |||
for (int i = 0; i < fDevNumInChans; i++) { | |||
free(fCAInputData->mBuffers[i].mData); | |||
} | |||
free(fCAInputData); | |||
} | |||
} | |||
int Open(dsp* dsp, int inChan, int outChan, int buffersize, int samplerate) | |||
{ | |||
fDSP = dsp; | |||
fDevNumInChans = inChan; | |||
fDevNumOutChans = outChan; | |||
// Initialize and configure the audio session | |||
OSStatus err = AudioSessionInitialize(NULL, NULL, InterruptionListener, this); | |||
if (err != noErr && err != kAudioSessionAlreadyInitialized) { | |||
printf("Couldn't initialize audio session\n"); | |||
printError(err); | |||
return OPEN_ERR; | |||
} | |||
if (SetParameters(buffersize, samplerate) < 0) { | |||
printf("Cannot set parameters to CoreAudio device\n"); | |||
return OPEN_ERR; | |||
} | |||
return NO_ERR; | |||
} | |||
int Close() | |||
{ | |||
AudioUnitUninitialize(fAUHAL); | |||
AudioComponentInstanceDispose(fAUHAL); | |||
return NO_ERR; | |||
} | |||
int Start() | |||
{ | |||
AudioSessionSetActive(true); | |||
if (AudioOutputUnitStart(fAUHAL) != noErr) { | |||
printf("Error while opening device : device open error\n"); | |||
return OPEN_ERR; | |||
} else { | |||
return NO_ERR; | |||
} | |||
} | |||
int Stop() | |||
{ | |||
AudioSessionSetActive(false); | |||
if (AudioOutputUnitStop(fAUHAL) != noErr) { | |||
printf("Error while closing device : device close error\n"); | |||
return OPEN_ERR; | |||
} else { | |||
return NO_ERR; | |||
} | |||
} | |||
}; | |||
/****************************************************************************** | |||
******************************************************************************* | |||
CORE AUDIO INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
class iosaudio : public audio { | |||
protected: | |||
TiPhoneCoreAudioRenderer fAudioDevice; | |||
int fSampleRate, fBufferSize; | |||
public: | |||
iosaudio(int srate, int bsize) : fSampleRate(srate), fBufferSize(bsize) {} | |||
virtual ~iosaudio() { fAudioDevice.Close(); } | |||
virtual bool init(const char* /*name*/, dsp* DSP) | |||
{ | |||
DSP->init(fSampleRate); | |||
if (fAudioDevice.Open(DSP, DSP->getNumInputs(), DSP->getNumOutputs(), fBufferSize, fSampleRate) < 0) { | |||
printf("Cannot open iOS audio device\n"); | |||
return false; | |||
} | |||
return true; | |||
} | |||
virtual bool start() | |||
{ | |||
if (fAudioDevice.Start() < 0) { | |||
printf("Cannot start iOS audio device\n"); | |||
return false; | |||
} | |||
return true; | |||
} | |||
virtual void stop() | |||
{ | |||
fAudioDevice.Stop(); | |||
} | |||
virtual int get_buffer_size() { return fBufferSize; } | |||
virtual int get_sample_rate() { return fSampleRate; } | |||
}; | |||
#endif | |||
/********************END ARCHITECTURE SECTION (part 2/2)****************/ | |||
@@ -0,0 +1,135 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
/****************************************************************************** | |||
******************************************************************************* | |||
A dummy audio driver | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
#ifndef __dummy_audio__ | |||
#define __dummy_audio__ | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <stdio.h> | |||
#include "faust/dsp/dsp.h" | |||
#include "faust/audio/audio.h" | |||
#define BUFFER_TO_RENDER 10 | |||
class dummy_audio : public audio { | |||
private: | |||
dsp* fDSP; | |||
long fSampleRate; | |||
long fBufferSize; | |||
FAUSTFLOAT** fInChannel; | |||
FAUSTFLOAT** fOutChannel; | |||
int fCount; | |||
public: | |||
dummy_audio(int count = 10) | |||
:fSampleRate(48000), fBufferSize(512), fCount(count) {} | |||
dummy_audio(int srate, int bsize, int count = 10) | |||
:fSampleRate(srate), fBufferSize(bsize), fCount(count) {} | |||
virtual ~dummy_audio() | |||
{ | |||
for (int i = 0; i < fDSP->getNumInputs(); i++) { | |||
delete[] fInChannel[i]; | |||
} | |||
for (int i = 0; i < fDSP->getNumOutputs(); i++) { | |||
delete[] fOutChannel[i]; | |||
} | |||
delete [] fInChannel; | |||
delete [] fOutChannel; | |||
} | |||
virtual bool init(const char* name, dsp* dsp) | |||
{ | |||
fDSP = dsp; | |||
fDSP->init(fSampleRate); | |||
fInChannel = new FAUSTFLOAT*[fDSP->getNumInputs()]; | |||
fOutChannel = new FAUSTFLOAT*[fDSP->getNumOutputs()]; | |||
for (int i = 0; i < fDSP->getNumInputs(); i++) { | |||
fInChannel[i] = new FAUSTFLOAT[fBufferSize]; | |||
memset(fInChannel[i], 0, sizeof(FAUSTFLOAT) * fBufferSize); | |||
} | |||
for (int i = 0; i < fDSP->getNumOutputs(); i++) { | |||
fOutChannel[i] = new FAUSTFLOAT[fBufferSize]; | |||
memset(fOutChannel[i], 0, sizeof(FAUSTFLOAT) * fBufferSize); | |||
} | |||
return true; | |||
} | |||
virtual bool start() | |||
{ | |||
while (--fCount > 0) { | |||
printf("Render one buffer\n"); | |||
render(); | |||
} | |||
return true; | |||
} | |||
virtual void stop() | |||
{} | |||
void render() | |||
{ | |||
fDSP->compute(fBufferSize, fInChannel, fOutChannel); | |||
if (fDSP->getNumInputs() > 0) { | |||
printf("First in = %f \n", fInChannel[0][0]); | |||
} | |||
if (fDSP->getNumOutputs() > 0) { | |||
printf("First out = %f \n", fOutChannel[0][0]); | |||
} | |||
} | |||
virtual int get_buffer_size() { return 0; } | |||
virtual int get_sample_rate() { return 0; } | |||
}; | |||
#endif |
@@ -0,0 +1,597 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __jack_dsp__ | |||
#define __jack_dsp__ | |||
#include <stdio.h> | |||
#include <cstdlib> | |||
#include <list> | |||
#include <vector> | |||
#include <string.h> | |||
#include <jack/jack.h> | |||
#include <jack/midiport.h> | |||
#ifdef JACK_IOS | |||
#include <jack/custom.h> | |||
#endif | |||
#include "faust/audio/audio.h" | |||
#include "faust/dsp/dsp.h" | |||
#include "faust/midi/jack-midi.h" | |||
#if defined(_WIN32) && !defined(__MINGW32__) | |||
#define snprintf _snprintf_s | |||
#endif | |||
/****************************************************************************** | |||
******************************************************************************* | |||
JACK AUDIO INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
class jackaudio : public audio { | |||
protected: | |||
dsp* fDSP; // FAUST DSP | |||
jack_client_t* fClient; // JACK client | |||
std::vector<jack_port_t*> fInputPorts; // JACK input ports | |||
std::vector<jack_port_t*> fOutputPorts; // JACK output ports | |||
std::vector<char*> fPhysicalInputs; | |||
std::vector<char*> fPhysicalOutputs; | |||
shutdown_callback fShutdown; // Shutdown callback to be called by libjack | |||
void* fShutdownArg; // Shutdown callback data | |||
void* fIconData; // iOS specific | |||
int fIconSize; // iOS specific | |||
bool fAutoConnect; // autoconnect with system in/out ports | |||
std::list<std::pair<std::string, std::string> > fConnections; // Connections list | |||
static int _jack_srate(jack_nframes_t nframes, void* arg) | |||
{ | |||
fprintf(stdout, "The sample rate is now %u/sec\n", nframes); | |||
return 0; | |||
} | |||
static void _jack_shutdown(void* arg) | |||
{} | |||
static void _jack_info_shutdown(jack_status_t code, const char* reason, void* arg) | |||
{ | |||
fprintf(stderr, "%s\n", reason); | |||
static_cast<jackaudio*>(arg)->shutdown(reason); | |||
} | |||
static int _jack_process(jack_nframes_t nframes, void* arg) | |||
{ | |||
return static_cast<jackaudio*>(arg)->process(nframes); | |||
} | |||
static int _jack_buffersize(jack_nframes_t nframes, void* arg) | |||
{ | |||
fprintf(stdout, "The buffer size is now %u/sec\n", nframes); | |||
return 0; | |||
} | |||
#ifdef _OPENMP | |||
static void* _jack_thread(void* arg) | |||
{ | |||
jackaudio* audio = (jackaudio*)arg; | |||
audio->process_thread(); | |||
return 0; | |||
} | |||
#endif | |||
void shutdown(const char* message) | |||
{ | |||
fClient = NULL; | |||
if (fShutdown) { | |||
fShutdown(message, fShutdownArg); | |||
} else { | |||
exit(1); // By default | |||
} | |||
} | |||
// Save client connections | |||
virtual bool save_connections() | |||
{ | |||
if (fClient) { | |||
fConnections.clear(); | |||
for (int i = 0; i < fInputPorts.size(); i++) { | |||
const char** connected_port = jack_port_get_all_connections(fClient, fInputPorts[i]); | |||
if (connected_port != NULL) { | |||
for (int port = 0; connected_port[port]; port++) { | |||
fConnections.push_back(std::make_pair(connected_port[port], jack_port_name(fInputPorts[i]))); | |||
// printf("INPUT %s ==> %s\n", connected_port[port], jack_port_name(fInputPorts[i])); | |||
} | |||
jack_free(connected_port); | |||
} | |||
} | |||
for (int i = 0; i < fOutputPorts.size(); i++) { | |||
const char** connected_port = jack_port_get_all_connections(fClient, fOutputPorts[i]); | |||
if (connected_port != NULL) { | |||
for (int port = 0; connected_port[port]; port++) { | |||
fConnections.push_back(std::make_pair(jack_port_name(fOutputPorts[i]), connected_port[port])); | |||
// printf("OUTPUT %s ==> %s\n", jack_port_name(fOutputPorts[i]), connected_port[port]); | |||
} | |||
jack_free(connected_port); | |||
} | |||
} | |||
return true; | |||
} else { | |||
fprintf(stdout, "Client no more running...\n"); | |||
return false; | |||
} | |||
} | |||
// Load client connections | |||
void load_connections() | |||
{ | |||
std::list<std::pair<std::string, std::string> >::const_iterator it; | |||
for (it = fConnections.begin(); it != fConnections.end(); it++) { | |||
std::pair<std::string, std::string> connection = *it; | |||
jack_connect(fClient, connection.first.c_str(), connection.second.c_str()); | |||
} | |||
} | |||
#ifdef _OPENMP | |||
void process_thread() | |||
{ | |||
jack_nframes_t nframes; | |||
while (1) { | |||
nframes = jack_cycle_wait(fClient); | |||
process(nframes); | |||
jack_cycle_signal(fClient, 0); | |||
} | |||
} | |||
#endif | |||
// JACK callbacks | |||
virtual int process(jack_nframes_t nframes) | |||
{ | |||
AVOIDDENORMALS; | |||
// Retrieve JACK inputs/output audio buffers | |||
float** fInChannel = (float**)alloca(fInputPorts.size() * sizeof(float*)); | |||
for (int i = 0; i < fInputPorts.size(); i++) { | |||
fInChannel[i] = (float*)jack_port_get_buffer(fInputPorts[i], nframes); | |||
} | |||
float** fOutChannel = (float**)alloca(fOutputPorts.size() * sizeof(float*)); | |||
for (int i = 0; i < fOutputPorts.size(); i++) { | |||
fOutChannel[i] = (float*)jack_port_get_buffer(fOutputPorts[i], nframes); | |||
} | |||
fDSP->compute(nframes, fInChannel, fOutChannel); | |||
return 0; | |||
} | |||
public: | |||
jackaudio(const void* icon_data = 0, size_t icon_size = 0, bool auto_connect = true) | |||
: fDSP(0), fClient(0), fShutdown(0), fShutdownArg(0), fAutoConnect(auto_connect) | |||
{ | |||
if (icon_data) { | |||
fIconData = malloc(icon_size); | |||
fIconSize = icon_size; | |||
memcpy(fIconData, icon_data, icon_size); | |||
} else { | |||
fIconData = NULL; | |||
fIconSize = 0; | |||
} | |||
} | |||
virtual ~jackaudio() | |||
{ | |||
if (fClient) { | |||
stop(); | |||
for (int i = 0; i < fInputPorts.size(); i++) { | |||
jack_port_unregister(fClient, fInputPorts[i]); | |||
} | |||
for (int i = 0; i < fOutputPorts.size(); i++) { | |||
jack_port_unregister(fClient, fOutputPorts[i]); | |||
} | |||
jack_client_close(fClient); | |||
if (fIconData) { | |||
free(fIconData); | |||
} | |||
} | |||
} | |||
virtual bool init(const char* name, dsp* dsp) | |||
{ | |||
if (init(name)) { | |||
if (dsp) { set_dsp(dsp); } | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
virtual bool init(const char* name) | |||
{ | |||
if ((fClient = jack_client_open(name, JackNullOption, NULL)) == 0) { | |||
fprintf(stderr, "JACK server not running ?\n"); | |||
return false; | |||
} | |||
#ifdef JACK_IOS | |||
jack_custom_publish_data(fClient, "icon.png", fIconData, fIconSize); | |||
#endif | |||
#ifdef _OPENMP | |||
jack_set_process_thread(fClient, _jack_thread, this); | |||
#else | |||
jack_set_process_callback(fClient, _jack_process, this); | |||
#endif | |||
jack_set_sample_rate_callback(fClient, _jack_srate, this); | |||
jack_set_buffer_size_callback(fClient, _jack_buffersize, this); | |||
jack_on_info_shutdown(fClient, _jack_info_shutdown, this); | |||
// Get Physical inputs | |||
int inputs = 0; | |||
char** physicalInPorts = (char**)jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsOutput); | |||
if (physicalInPorts != NULL) { | |||
while (physicalInPorts[inputs]) { | |||
fPhysicalInputs.push_back(physicalInPorts[inputs]); | |||
printf("physical input %s\n", physicalInPorts[inputs]); | |||
inputs++; | |||
} | |||
jack_free(physicalInPorts); | |||
} | |||
// Get Physical outputs | |||
int outputs = 0; | |||
char** physicalOutPorts = (char**)jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsInput); | |||
if (physicalOutPorts != NULL) { | |||
while (physicalOutPorts[outputs]) { | |||
fPhysicalOutputs.push_back(physicalOutPorts[outputs]); | |||
printf("physical output %s\n", physicalOutPorts[outputs]); | |||
outputs++; | |||
} | |||
jack_free(physicalOutPorts); | |||
} | |||
return true; | |||
} | |||
virtual bool start() | |||
{ | |||
if (jack_activate(fClient)) { | |||
fprintf(stderr, "Cannot activate client\n"); | |||
return false; | |||
} | |||
if (fConnections.size() > 0) { | |||
load_connections(); | |||
} else if (fAutoConnect) { | |||
default_connections(); | |||
} | |||
return true; | |||
} | |||
virtual void stop() | |||
{ | |||
if (fClient) { | |||
save_connections(); | |||
jack_deactivate(fClient); | |||
} | |||
} | |||
virtual void shutdown(shutdown_callback cb, void* arg) | |||
{ | |||
fShutdown = cb; | |||
fShutdownArg = arg; | |||
} | |||
virtual int get_buffer_size() { return jack_get_buffer_size(fClient); } | |||
virtual int get_sample_rate() { return jack_get_sample_rate(fClient); } | |||
virtual int get_num_inputs() | |||
{ | |||
return fPhysicalInputs.size(); | |||
} | |||
virtual int get_num_outputs() | |||
{ | |||
return fPhysicalOutputs.size(); | |||
} | |||
// Additional public API | |||
jack_client_t* get_client() { return fClient; } | |||
// Connect to physical inputs/outputs | |||
void default_connections() | |||
{ | |||
// To avoid feedback at launch time, don't connect hardware inputs | |||
/* | |||
for (int i = 0; i < fInputPorts.size() && i < fPhysicalOutputs.size(); i++) { | |||
jack_connect(fClient, fPhysicalInputs[i], jack_port_name(fInputPorts[i])); | |||
} | |||
*/ | |||
for (int i = 0; i < fOutputPorts.size() && i < fPhysicalInputs.size(); i++) { | |||
jack_connect(fClient, jack_port_name(fOutputPorts[i]), fPhysicalOutputs[i]); | |||
} | |||
} | |||
virtual void set_dsp(dsp* dsp) | |||
{ | |||
fDSP = dsp; | |||
for (int i = 0; i < fDSP->getNumInputs(); i++) { | |||
char buf[256]; | |||
snprintf(buf, 256, "in_%d", i); | |||
fInputPorts.push_back(jack_port_register(fClient, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)); | |||
} | |||
for (int i = 0; i < fDSP->getNumOutputs(); i++) { | |||
char buf[256]; | |||
snprintf(buf, 256, "out_%d", i); | |||
fOutputPorts.push_back(jack_port_register(fClient, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)); | |||
} | |||
fDSP->init(jack_get_sample_rate(fClient)); | |||
} | |||
void connect(jackaudio* driver, int src, int dst, bool reverse) | |||
{ | |||
if (driver) { | |||
// Connection between drivers | |||
jack_port_t* src_port = get_output_port(src); | |||
jack_port_t* dst_port = driver->get_input_port(src); | |||
if (src_port && dst_port) { | |||
jack_connect(fClient, jack_port_name(src_port), jack_port_name(dst_port)); | |||
} | |||
} else if (reverse) { | |||
// Connection to physical input | |||
if (src > fPhysicalInputs.size()) return; | |||
jack_port_t* dst_port = get_input_port(dst); | |||
if (dst_port) { | |||
jack_connect(fClient, fPhysicalInputs[src], jack_port_name(dst_port)); | |||
} | |||
} else { | |||
// Connection to physical output | |||
if (dst > fPhysicalOutputs.size()) return; | |||
jack_port_t* src_port = get_output_port(src); | |||
if (src_port) { | |||
jack_connect(fClient, jack_port_name(src_port), fPhysicalOutputs[dst]); | |||
} | |||
} | |||
} | |||
void disconnect(jackaudio* driver, int src, int dst, bool reverse) | |||
{ | |||
if (driver) { | |||
// Connection between drivers | |||
jack_port_t* src_port = get_output_port(src); | |||
jack_port_t* dst_port = driver->get_input_port(src); | |||
if (src_port && dst_port) { | |||
jack_disconnect(fClient, jack_port_name(src_port), jack_port_name(dst_port)); | |||
} | |||
} else if (reverse) { | |||
// Connection to physical input | |||
if (src > fPhysicalInputs.size()) return; | |||
jack_port_t* dst_port = get_input_port(dst); | |||
if (dst_port) { | |||
jack_disconnect(fClient, fPhysicalInputs[src], jack_port_name(dst_port)); | |||
} | |||
} else { | |||
// Connection to physical output | |||
if (dst > fPhysicalOutputs.size()) return; | |||
jack_port_t* src_port = get_output_port(src); | |||
if (src_port) { | |||
jack_disconnect(fClient, jack_port_name(src_port), fPhysicalOutputs[dst]); | |||
} | |||
} | |||
} | |||
bool is_connected(jackaudio* driver, int src, int dst, bool reverse) | |||
{ | |||
if (driver) { | |||
// Connection between drivers | |||
jack_port_t* src_port = get_output_port(src); | |||
jack_port_t* dst_port = driver->get_input_port(src); | |||
if (src_port && dst_port) { | |||
return jack_port_connected_to(src_port, jack_port_name(dst_port)); | |||
} else { | |||
return false; | |||
} | |||
} else if (reverse) { | |||
// Connection to physical input | |||
if (src > fPhysicalInputs.size()) return false; | |||
jack_port_t* dst_port = get_input_port(dst); | |||
if (dst_port) { | |||
return jack_port_connected_to(dst_port, fPhysicalInputs[src]); | |||
} else { | |||
return false; | |||
} | |||
} else { | |||
// Connection to physical output | |||
if (dst > fPhysicalOutputs.size()) return false; | |||
jack_port_t* src_port = get_output_port(src); | |||
if (src_port) { | |||
return jack_port_connected_to(src_port, fPhysicalOutputs[dst]); | |||
} else { | |||
return false; | |||
} | |||
} | |||
} | |||
jack_port_t* get_input_port(int port) { return (port >= 0 && port < fInputPorts.size()) ? fInputPorts[port] : 0; } | |||
jack_port_t* get_output_port(int port) { return (port >= 0 && port < fOutputPorts.size()) ? fOutputPorts[port] : 0; } | |||
}; | |||
// Add JACK MIDI | |||
class jackaudio_midi : public jackaudio, public jack_midi_handler { | |||
protected: | |||
jack_port_t* fInputMidiPort; // JACK input MIDI port | |||
jack_port_t* fOutputMidiPort; // JACK output MIDI port | |||
virtual bool save_connections() | |||
{ | |||
if (jackaudio::save_connections()) { // Audio connections can be saved, so try MIDI | |||
if (fInputMidiPort) { | |||
const char** connected_port = jack_port_get_all_connections(fClient, fInputMidiPort); | |||
if (connected_port != NULL) { | |||
for (int port = 0; connected_port[port]; port++) { | |||
fConnections.push_back(std::make_pair(connected_port[port], jack_port_name(fInputMidiPort))); | |||
// printf("INPUT %s ==> %s\n", connected_port[port], jack_port_name(fInputPorts[i])); | |||
} | |||
jack_free(connected_port); | |||
} | |||
} | |||
if (fOutputMidiPort) { | |||
const char** connected_port = jack_port_get_all_connections(fClient, fOutputMidiPort); | |||
if (connected_port != NULL) { | |||
for (int port = 0; connected_port[port]; port++) { | |||
fConnections.push_back(std::make_pair(jack_port_name(fOutputMidiPort), connected_port[port])); | |||
// printf("OUTPUT %s ==> %s\n", jack_port_name(fOutputPorts[i]), connected_port[port]); | |||
} | |||
jack_free(connected_port); | |||
} | |||
} | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
virtual void processMidiIn(jack_nframes_t nframes) | |||
{ | |||
// MIDI input | |||
if (fInputMidiPort) { | |||
processMidiInBuffer(jack_port_get_buffer(fInputMidiPort, nframes)); | |||
} | |||
} | |||
virtual void processAudio(jack_nframes_t nframes) | |||
{ | |||
// Audio | |||
AVOIDDENORMALS; | |||
// Retrieve JACK inputs/output audio buffers | |||
float** fInChannel = (float**)alloca(fInputPorts.size() * sizeof(float*)); | |||
for (int i = 0; i < fInputPorts.size(); i++) { | |||
fInChannel[i] = (float*)jack_port_get_buffer(fInputPorts[i], nframes); | |||
} | |||
float** fOutChannel = (float**)alloca(fOutputPorts.size() * sizeof(float*)); | |||
for (int i = 0; i < fOutputPorts.size(); i++) { | |||
fOutChannel[i] = (float*)jack_port_get_buffer(fOutputPorts[i], nframes); | |||
} | |||
// By convention timestamp of -1 means 'no timestamp conversion' : events already have a timestamp espressed in frames | |||
fDSP->compute(-1, nframes, fInChannel, fOutChannel); | |||
} | |||
virtual void processMidiOut(jack_nframes_t nframes) | |||
{ | |||
// MIDI output | |||
if (fOutputMidiPort) { | |||
processMidiOutBuffer(jack_port_get_buffer(fOutputMidiPort, nframes)); | |||
} | |||
} | |||
virtual int process(jack_nframes_t nframes) | |||
{ | |||
// MIDI in | |||
processMidiIn(nframes); | |||
// Audio | |||
processAudio(nframes); | |||
// MIDI out | |||
processMidiOut(nframes); | |||
return 0; | |||
} | |||
public: | |||
jackaudio_midi(const void* icon_data = 0, size_t icon_size = 0, bool auto_connect = true) | |||
:jackaudio(icon_data, icon_size, auto_connect), jack_midi_handler("JACKMidi"), | |||
fInputMidiPort(0), fOutputMidiPort(0) | |||
{} | |||
virtual ~jackaudio_midi() | |||
{ | |||
if (fClient) { | |||
if (fInputMidiPort) { jack_port_unregister(fClient, fInputMidiPort); } | |||
if (fOutputMidiPort) { jack_port_unregister(fClient, fOutputMidiPort); } | |||
} | |||
} | |||
virtual bool init(const char* name, dsp* dsp, bool midi = false) | |||
{ | |||
if (jackaudio::init(name)) { | |||
if (dsp) { set_dsp(dsp); } | |||
if (midi) { | |||
fInputMidiPort = jack_port_register(fClient, "midi_in_1", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | |||
fOutputMidiPort = jack_port_register(fClient, "midi_out_1", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); | |||
} | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
virtual bool start() | |||
{ | |||
return jackaudio::start(); | |||
} | |||
virtual void stop() | |||
{ | |||
jackaudio::stop(); | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,357 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __netjack_dsp__ | |||
#define __netjack_dsp__ | |||
#include <stdio.h> | |||
#include "faust/audio/audio.h" | |||
#include "faust/dsp/dsp.h" | |||
#include "faust/gui/ControlUI.h" | |||
#include "faust/midi/jack-midi.h" | |||
#include <jack/net.h> | |||
#include <string> | |||
#include <assert.h> | |||
class netjackaudio : public audio | |||
{ | |||
protected: | |||
dsp* fDSP; | |||
jack_net_slave_t* fNet; | |||
int fNetFormat; | |||
std::string fMasterIP; | |||
int fMasterPort; | |||
int fMTU; | |||
int fLatency; | |||
jack_master_t fResult; | |||
#ifdef RESTART_CB_API | |||
static int net_restart(void* arg) | |||
{ | |||
printf("Network failure, restart...\n"); | |||
return static_cast<netjackaudio*>(arg)->restart_cb(); | |||
} | |||
#else | |||
static void net_shutdown(void* arg) | |||
{ | |||
printf("Network failure, shutdown...\n"); | |||
static_cast<netjackaudio*>(arg)->shutdown_cb(); | |||
} | |||
#endif | |||
static int net_sample_rate(jack_nframes_t nframes, void* arg) | |||
{ | |||
return static_cast<netjackaudio*>(arg)->set_sample_rate(nframes); | |||
} | |||
static int net_buffer_size(jack_nframes_t nframes, void* arg) | |||
{ | |||
return static_cast<netjackaudio*>(arg)->set_buffer_size(nframes); | |||
} | |||
static void net_error(int error_code, void* arg) | |||
{ | |||
return static_cast<netjackaudio*>(arg)->error_cb(error_code); | |||
} | |||
static int net_process(jack_nframes_t buffer_size, | |||
int, | |||
float** audio_inputs, | |||
int, | |||
void** midi_inputs, | |||
int, | |||
float** audio_outputs, | |||
int, | |||
void** midi_outputs, | |||
void* arg) | |||
{ | |||
static_cast<netjackaudio*>(arg)->process(buffer_size, audio_inputs, audio_outputs, midi_inputs, midi_outputs); | |||
return 0; | |||
} | |||
bool init_aux(const char* name, dsp* DSP, int audio_inputs, int audio_outputs, int midi_inputs, int midi_outputs) | |||
{ | |||
if (init_aux(name, audio_inputs, audio_outputs, midi_inputs, midi_outputs)) { | |||
set_dsp(DSP); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
bool init_aux(const char* name, int audio_inputs, int audio_outputs, int midi_inputs, int midi_outputs) | |||
{ | |||
jack_slave_t request = { | |||
audio_inputs, | |||
audio_outputs, | |||
midi_inputs, | |||
midi_outputs, | |||
fMTU, | |||
2, | |||
(fNetFormat > 0) ? JackOpusEncoder : ((fNetFormat == -1) ? JackFloatEncoder : JackIntEncoder), | |||
(fNetFormat > 0) ? fNetFormat : 0, | |||
fLatency | |||
}; | |||
if ((fNet = jack_net_slave_open(fMasterIP.c_str(), fMasterPort, name, &request, &fResult)) == 0) { | |||
printf("JACK remote server not running ?\n"); | |||
return false; | |||
} | |||
jack_set_net_slave_process_callback(fNet, net_process, this); | |||
#ifdef RESTART_CB_API | |||
jack_set_net_slave_restart_callback(fNet, net_restart, this); | |||
#else | |||
jack_set_net_slave_shutdown_callback(fNet, net_shutdown, this); | |||
#endif | |||
jack_set_net_slave_sample_rate_callback(fNet, net_sample_rate, this); | |||
jack_set_net_slave_buffer_size_callback(fNet, net_buffer_size, this); | |||
jack_set_net_slave_error_callback(fNet, net_error, this); | |||
return true; | |||
} | |||
// Possibly to be redefined by subclasses | |||
virtual int restart_cb() | |||
{ | |||
return 0; | |||
} | |||
virtual void shutdown_cb() | |||
{} | |||
virtual void error_cb(int error_code) | |||
{} | |||
virtual int set_sample_rate(jack_nframes_t nframes) | |||
{ | |||
return 0; | |||
} | |||
virtual int set_buffer_size(jack_nframes_t nframes) | |||
{ | |||
return 0; | |||
} | |||
virtual void process(int count, float** audio_inputs, float** audio_outputs, void** midi_inputs, void** midi_outputs) | |||
{ | |||
AVOIDDENORMALS; | |||
fDSP->compute(count, audio_inputs, audio_outputs); | |||
} | |||
public: | |||
netjackaudio(int net_format, const std::string& master_ip, int master_port, int mtu, int latency = 2) | |||
: fDSP(0), fNet(0), fNetFormat(net_format), fMasterIP(master_ip), fMasterPort(master_port), fMTU(mtu), fLatency(latency) | |||
{} | |||
virtual ~netjackaudio() | |||
{ | |||
if (fNet) { | |||
stop(); | |||
jack_net_slave_close(fNet); | |||
} | |||
} | |||
virtual bool init(const char* name, dsp* DSP) | |||
{ | |||
return init_aux(name, DSP, DSP->getNumInputs(), DSP->getNumOutputs(), 0, 0); | |||
} | |||
virtual bool start() | |||
{ | |||
if (jack_net_slave_activate(fNet)) { | |||
printf("cannot activate net"); | |||
return false; | |||
} | |||
return true; | |||
} | |||
virtual void stop() | |||
{ | |||
if (fNet) { | |||
jack_net_slave_deactivate(fNet); | |||
} | |||
} | |||
void set_dsp(dsp* DSP) | |||
{ | |||
fDSP = DSP; | |||
fDSP->init(fResult.sample_rate); | |||
} | |||
virtual int get_buffer_size() { return fResult.buffer_size; } | |||
virtual int get_sample_rate() { return fResult.sample_rate; } | |||
}; | |||
/* | |||
A special NetJack client that uses one more audio input/output to transmit control values. | |||
*/ | |||
class netjackaudio_control : public netjackaudio, public ControlUI { | |||
protected: | |||
virtual void process(int count, float** audio_inputs, float** audio_outputs, void** midi_inputs, void** midi_outputs) | |||
{ | |||
AVOIDDENORMALS; | |||
float** inputs_tmp = (float**)alloca(fDSP->getNumInputs()*sizeof(float*)); | |||
float** outputs_tmp = (float**)alloca(fDSP->getNumOutputs()*sizeof(float*)); | |||
for (int i = 0; i < fDSP->getNumInputs(); i++) { | |||
inputs_tmp[i] = audio_inputs[i+1]; | |||
} | |||
for (int i = 0; i < fDSP->getNumOutputs(); i++) { | |||
outputs_tmp[i] = audio_outputs[i+1]; | |||
} | |||
// Control buffer always use buffer_size, even if uncomplete data buffer (count < buffer_size) is received | |||
decode_control(audio_inputs[0], fResult.buffer_size); | |||
// "count" may be less than buffer_size | |||
fDSP->compute(count, inputs_tmp, outputs_tmp); | |||
// Control buffer always use buffer_size, even if uncomplete data buffer (count < buffer_size) is received | |||
encode_control(audio_outputs[0], fResult.buffer_size); | |||
} | |||
public: | |||
netjackaudio_control(int net_format, const std::string& master_ip, int master_port, int mtu, int latency) | |||
:netjackaudio(net_format, master_ip, master_port, mtu, latency) | |||
{} | |||
virtual ~netjackaudio_control() | |||
{} | |||
bool is_connexion_active() | |||
{ | |||
return jack_net_slave_is_active(fNet); | |||
} | |||
virtual bool init(const char* name, dsp* dsp) | |||
{ | |||
dsp->buildUserInterface(this); | |||
return init_aux(name, dsp, dsp->getNumInputs() + 1, dsp->getNumOutputs() + 1, 0, 0); // One more audio port for control | |||
} | |||
virtual int restart_cb() | |||
{ | |||
return -1; | |||
} | |||
}; | |||
/* | |||
A special NetJack client that uses MIDI input/output to transmit control values. | |||
*/ | |||
class netjackaudio_midicontrol : public netjackaudio, public ControlUI, public jack_midi_handler { | |||
protected: | |||
virtual void process(int count, float** audio_inputs, float** audio_outputs, void** midi_inputs, void** midi_outputs) | |||
{ | |||
AVOIDDENORMALS; | |||
float** inputs_tmp = (float**)alloca(fDSP->getNumInputs()*sizeof(float*)); | |||
float** outputs_tmp = (float**)alloca(fDSP->getNumOutputs()*sizeof(float*)); | |||
for (int i = 0; i < fDSP->getNumInputs(); i++) { | |||
inputs_tmp[i] = audio_inputs[i]; | |||
} | |||
for (int i = 0; i < fDSP->getNumOutputs(); i++) { | |||
outputs_tmp[i] = audio_outputs[i]; | |||
} | |||
// Control buffer always use buffer_size, even if uncomplete data buffer (count < buffer_size) is received | |||
decode_midi_control(midi_inputs[0], fResult.buffer_size); | |||
// Decode MIDI messages | |||
processMidiInBuffer(midi_inputs[1]); | |||
// "count" may be less than buffer_size | |||
fDSP->compute(count, inputs_tmp, outputs_tmp); | |||
// Control buffer always use buffer_size, even if uncomplete data buffer (count < buffer_size) is received | |||
encode_midi_control(midi_outputs[0], fResult.buffer_size); | |||
// Encode MIDI messages | |||
processMidiOutBuffer(midi_outputs[1], true); | |||
} | |||
public: | |||
netjackaudio_midicontrol(int net_format, const std::string& master_ip, int master_port, int mtu, int latency) | |||
:netjackaudio(net_format, master_ip, master_port, mtu, latency) | |||
{} | |||
virtual ~netjackaudio_midicontrol() | |||
{} | |||
bool is_connexion_active() | |||
{ | |||
return jack_net_slave_is_active(fNet); | |||
} | |||
virtual bool init(const char* name, dsp* dsp) | |||
{ | |||
dsp->buildUserInterface(this); | |||
// One MIDI channel for control, and one MIDI channel for messages in both direction | |||
return init_aux(name, dsp, dsp->getNumInputs(), dsp->getNumOutputs(), 2, 2); | |||
} | |||
virtual int restart_cb() | |||
{ | |||
return -1; | |||
} | |||
}; | |||
#endif | |||
/********************END ARCHITECTURE SECTION (part 2/2)****************/ | |||
@@ -0,0 +1,641 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2014 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
/* | |||
* This is an interface for OpenSL ES to make it easier to use with Android | |||
* devices. | |||
*/ | |||
#include <SLES/OpenSLES.h> | |||
#include <SLES/OpenSLES_Android.h> | |||
#include <android/log.h> | |||
#include <pthread.h> | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
typedef struct threadLock_ { | |||
pthread_mutex_t m; | |||
pthread_cond_t c; | |||
unsigned char s; | |||
} threadLock; | |||
typedef struct opensl_stream { | |||
// engine interfaces | |||
SLObjectItf engineObject; | |||
SLEngineItf engineEngine; | |||
// output mix interfaces | |||
SLObjectItf outputMixObject; | |||
// buffer queue player interfaces | |||
SLObjectItf bqPlayerObject; | |||
SLPlayItf bqPlayerPlay; | |||
SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; | |||
SLEffectSendItf bqPlayerEffectSend; | |||
// recorder interfaces | |||
SLObjectItf recorderObject; | |||
SLRecordItf recorderRecord; | |||
SLAndroidSimpleBufferQueueItf recorderBufferQueue; | |||
// buffer indexes | |||
int currentInputIndex; | |||
int currentOutputIndex; | |||
// current buffer half (0, 1) | |||
int currentOutputBuffer; | |||
int currentInputBuffer; | |||
// buffers | |||
short *outputBuffer[2]; | |||
short *inputBuffer[2]; | |||
// size of buffers | |||
int outBufSamples; | |||
int inBufSamples; | |||
// locks | |||
void* inlock; | |||
void* outlock; | |||
double time; | |||
int inchannels; | |||
int outchannels; | |||
int sr; | |||
} OPENSL_STREAM; | |||
#define CONV16BIT 32767.f | |||
#define CONVMYFLT (1.f/32767.f) | |||
static void* createThreadLock(void); | |||
static int waitThreadLock(void *lock); | |||
static void notifyThreadLock(void *lock); | |||
static void destroyThreadLock(void *lock); | |||
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context); | |||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context); | |||
// creates the OpenSL ES audio engine | |||
static SLresult openSLCreateEngine(OPENSL_STREAM *p) { | |||
SLresult result; | |||
// create engine | |||
result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL); | |||
if (result != SL_RESULT_SUCCESS) | |||
goto engine_end; | |||
// realize the engine | |||
result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE); | |||
if (result != SL_RESULT_SUCCESS) | |||
goto engine_end; | |||
// get the engine interface, which is needed in order to create other objects | |||
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, | |||
&(p->engineEngine)); | |||
if (result != SL_RESULT_SUCCESS) | |||
goto engine_end; | |||
engine_end: return result; | |||
} | |||
// opens the OpenSL ES device for output | |||
static SLresult openSLPlayOpen(OPENSL_STREAM *p) { | |||
SLresult result; | |||
SLuint32 sr = p->sr; | |||
SLuint32 channels = p->outchannels; | |||
if (channels) { | |||
// configure audio source | |||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { | |||
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 }; | |||
switch (sr) { | |||
case 8000: | |||
sr = SL_SAMPLINGRATE_8; | |||
break; | |||
case 11025: | |||
sr = SL_SAMPLINGRATE_11_025; | |||
break; | |||
case 16000: | |||
sr = SL_SAMPLINGRATE_16; | |||
break; | |||
case 22050: | |||
sr = SL_SAMPLINGRATE_22_05; | |||
break; | |||
case 24000: | |||
sr = SL_SAMPLINGRATE_24; | |||
break; | |||
case 32000: | |||
sr = SL_SAMPLINGRATE_32; | |||
break; | |||
case 44100: | |||
sr = SL_SAMPLINGRATE_44_1; | |||
break; | |||
case 48000: | |||
sr = SL_SAMPLINGRATE_48; | |||
break; | |||
case 64000: | |||
sr = SL_SAMPLINGRATE_64; | |||
break; | |||
case 88200: | |||
sr = SL_SAMPLINGRATE_88_2; | |||
break; | |||
case 96000: | |||
sr = SL_SAMPLINGRATE_96; | |||
break; | |||
case 192000: | |||
sr = SL_SAMPLINGRATE_192; | |||
break; | |||
default: | |||
return -1; | |||
} | |||
const SLInterfaceID ids[] = { SL_IID_VOLUME }; | |||
const SLboolean req[] = { SL_BOOLEAN_FALSE }; | |||
result = (*p->engineEngine)->CreateOutputMix(p->engineEngine, | |||
&(p->outputMixObject), 1, ids, req); | |||
//if(result != SL_RESULT_SUCCESS) goto end_openaudio; | |||
// realize the output mix | |||
result = (*p->outputMixObject)->Realize(p->outputMixObject, | |||
SL_BOOLEAN_FALSE); | |||
int speakers; | |||
if (channels > 1) | |||
speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; | |||
else | |||
speakers = SL_SPEAKER_FRONT_CENTER; | |||
SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM, channels, sr, | |||
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, | |||
speakers, SL_BYTEORDER_LITTLEENDIAN }; | |||
SLDataSource audioSrc = { &loc_bufq, &format_pcm }; | |||
// configure audio sink | |||
SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, | |||
p->outputMixObject }; | |||
SLDataSink audioSnk = { &loc_outmix, NULL }; | |||
// create audio player | |||
const SLInterfaceID ids1[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; | |||
const SLboolean req1[] = { SL_BOOLEAN_TRUE }; | |||
result = (*p->engineEngine)->CreateAudioPlayer(p->engineEngine, | |||
&(p->bqPlayerObject), &audioSrc, &audioSnk, 1, ids1, req1); | |||
if (result != SL_RESULT_SUCCESS) | |||
goto end_openaudio; | |||
// realize the player | |||
result = (*p->bqPlayerObject)->Realize(p->bqPlayerObject, | |||
SL_BOOLEAN_FALSE); | |||
if (result != SL_RESULT_SUCCESS) | |||
goto end_openaudio; | |||
// get the play interface | |||
result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, | |||
SL_IID_PLAY, &(p->bqPlayerPlay)); | |||
if (result != SL_RESULT_SUCCESS) | |||
goto end_openaudio; | |||
// get the buffer queue interface | |||
result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, | |||
SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(p->bqPlayerBufferQueue)); | |||
if (result != SL_RESULT_SUCCESS) | |||
goto end_openaudio; | |||
// register callback on the buffer queue | |||
result = (*p->bqPlayerBufferQueue)->RegisterCallback( | |||
p->bqPlayerBufferQueue, bqPlayerCallback, p); | |||
if (result != SL_RESULT_SUCCESS) | |||
goto end_openaudio; | |||
// set the player's state to playing | |||
result = (*p->bqPlayerPlay)->SetPlayState(p->bqPlayerPlay, | |||
SL_PLAYSTATE_PLAYING); | |||
end_openaudio: return result; | |||
} | |||
return SL_RESULT_SUCCESS; | |||
} | |||
// Open the OpenSL ES device for input | |||
static SLresult openSLRecOpen(OPENSL_STREAM *p) { | |||
SLresult result; | |||
SLuint32 sr = p->sr; | |||
SLuint32 channels = p->inchannels; | |||
if (channels) { | |||
switch (sr) { | |||
case 8000: | |||
sr = SL_SAMPLINGRATE_8; | |||
break; | |||
case 11025: | |||
sr = SL_SAMPLINGRATE_11_025; | |||
break; | |||
case 16000: | |||
sr = SL_SAMPLINGRATE_16; | |||
break; | |||
case 22050: | |||
sr = SL_SAMPLINGRATE_22_05; | |||
break; | |||
case 24000: | |||
sr = SL_SAMPLINGRATE_24; | |||
break; | |||
case 32000: | |||
sr = SL_SAMPLINGRATE_32; | |||
break; | |||
case 44100: | |||
sr = SL_SAMPLINGRATE_44_1; | |||
break; | |||
case 48000: | |||
sr = SL_SAMPLINGRATE_48; | |||
break; | |||
case 64000: | |||
sr = SL_SAMPLINGRATE_64; | |||
break; | |||
case 88200: | |||
sr = SL_SAMPLINGRATE_88_2; | |||
break; | |||
case 96000: | |||
sr = SL_SAMPLINGRATE_96; | |||
break; | |||
case 192000: | |||
sr = SL_SAMPLINGRATE_192; | |||
break; | |||
default: | |||
return -1; | |||
} | |||
// configure audio source | |||
SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, | |||
SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; | |||
SLDataSource audioSrc = { &loc_dev, NULL }; | |||
// configure audio sink | |||
int speakers; | |||
if (channels > 1) | |||
speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; | |||
else | |||
speakers = SL_SPEAKER_FRONT_CENTER; | |||
SLDataLocator_AndroidSimpleBufferQueue loc_bq = { | |||
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 }; | |||
SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM, channels, sr, | |||
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, | |||
speakers, SL_BYTEORDER_LITTLEENDIAN }; | |||
SLDataSink audioSnk = { &loc_bq, &format_pcm }; | |||
// create audio recorder | |||
// (requires the RECORD_AUDIO permission) | |||
const SLInterfaceID id[1] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; | |||
const SLboolean req[1] = { SL_BOOLEAN_TRUE }; | |||
result = (*p->engineEngine)->CreateAudioRecorder(p->engineEngine, | |||
&(p->recorderObject), &audioSrc, &audioSnk, 1, id, req); | |||
if (SL_RESULT_SUCCESS != result) | |||
goto end_recopen; | |||
// realize the audio recorder | |||
result = (*p->recorderObject)->Realize(p->recorderObject, | |||
SL_BOOLEAN_FALSE); | |||
if (SL_RESULT_SUCCESS != result) | |||
goto end_recopen; | |||
// get the record interface | |||
result = (*p->recorderObject)->GetInterface(p->recorderObject, | |||
SL_IID_RECORD, &(p->recorderRecord)); | |||
if (SL_RESULT_SUCCESS != result) | |||
goto end_recopen; | |||
// get the buffer queue interface | |||
result = (*p->recorderObject)->GetInterface(p->recorderObject, | |||
SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(p->recorderBufferQueue)); | |||
if (SL_RESULT_SUCCESS != result) | |||
goto end_recopen; | |||
// register callback on the buffer queue | |||
result = (*p->recorderBufferQueue)->RegisterCallback( | |||
p->recorderBufferQueue, bqRecorderCallback, p); | |||
if (SL_RESULT_SUCCESS != result) | |||
goto end_recopen; | |||
result = (*p->recorderRecord)->SetRecordState(p->recorderRecord, | |||
SL_RECORDSTATE_RECORDING); | |||
end_recopen: return result; | |||
} else | |||
return SL_RESULT_SUCCESS; | |||
} | |||
// close the OpenSL IO and destroy the audio engine | |||
static void openSLDestroyEngine(OPENSL_STREAM *p) { | |||
// destroy buffer queue audio player object, and invalidate all associated interfaces | |||
if (p->bqPlayerObject != NULL) { | |||
(*p->bqPlayerObject)->Destroy(p->bqPlayerObject); | |||
p->bqPlayerObject = NULL; | |||
p->bqPlayerPlay = NULL; | |||
p->bqPlayerBufferQueue = NULL; | |||
p->bqPlayerEffectSend = NULL; | |||
} | |||
// destroy audio recorder object, and invalidate all associated interfaces | |||
if (p->recorderObject != NULL) { | |||
(*p->recorderObject)->Destroy(p->recorderObject); | |||
p->recorderObject = NULL; | |||
p->recorderRecord = NULL; | |||
p->recorderBufferQueue = NULL; | |||
} | |||
// destroy output mix object, and invalidate all associated interfaces | |||
if (p->outputMixObject != NULL) { | |||
(*p->outputMixObject)->Destroy(p->outputMixObject); | |||
p->outputMixObject = NULL; | |||
} | |||
// destroy engine object, and invalidate all associated interfaces | |||
if (p->engineObject != NULL) { | |||
(*p->engineObject)->Destroy(p->engineObject); | |||
p->engineObject = NULL; | |||
p->engineEngine = NULL; | |||
} | |||
} | |||
// close the android audio device | |||
void android_CloseAudioDevice(OPENSL_STREAM *p) { | |||
if (p == NULL) | |||
return; | |||
openSLDestroyEngine(p); | |||
if (p->inlock != NULL) { | |||
notifyThreadLock(p->inlock); | |||
destroyThreadLock(p->inlock); | |||
p->inlock = NULL; | |||
} | |||
if (p->outlock != NULL) { | |||
notifyThreadLock(p->outlock); | |||
destroyThreadLock(p->outlock); | |||
p->inlock = NULL; | |||
} | |||
if (p->outputBuffer[0] != NULL) { | |||
free(p->outputBuffer[0]); | |||
p->outputBuffer[0] = NULL; | |||
} | |||
if (p->outputBuffer[1] != NULL) { | |||
free(p->outputBuffer[1]); | |||
p->outputBuffer[1] = NULL; | |||
} | |||
if (p->inputBuffer[0] != NULL) { | |||
free(p->inputBuffer[0]); | |||
p->inputBuffer[0] = NULL; | |||
} | |||
if (p->inputBuffer[1] != NULL) { | |||
free(p->inputBuffer[1]); | |||
p->inputBuffer[1] = NULL; | |||
} | |||
free(p); | |||
} | |||
// open the android audio device for input and/or output | |||
OPENSL_STREAM *android_OpenAudioDevice(int sr, int inchannels, int outchannels, | |||
int bufferframes) { | |||
OPENSL_STREAM *p; | |||
p = (OPENSL_STREAM *) calloc(sizeof(OPENSL_STREAM), 1); | |||
p->inchannels = inchannels; | |||
p->outchannels = outchannels; | |||
p->sr = sr; | |||
p->inlock = createThreadLock(); | |||
p->outlock = createThreadLock(); | |||
if ((p->outBufSamples = bufferframes * outchannels) != 0) { | |||
if ((p->outputBuffer[0] = (short *) calloc(p->outBufSamples, | |||
sizeof(short))) == NULL || (p->outputBuffer[1] = | |||
(short *) calloc(p->outBufSamples, sizeof(short))) == NULL) { | |||
android_CloseAudioDevice(p); | |||
return NULL; | |||
} | |||
} | |||
if ((p->inBufSamples = bufferframes * inchannels) != 0) { | |||
if ((p->inputBuffer[0] = (short *) calloc(p->inBufSamples, | |||
sizeof(short))) == NULL || (p->inputBuffer[1] = | |||
(short *) calloc(p->inBufSamples, sizeof(short))) == NULL) { | |||
android_CloseAudioDevice(p); | |||
return NULL; | |||
} | |||
} | |||
p->currentInputIndex = 0; | |||
p->currentOutputBuffer = 0; | |||
p->currentInputIndex = p->inBufSamples; | |||
p->currentInputBuffer = 0; | |||
if (openSLCreateEngine(p) != SL_RESULT_SUCCESS) { | |||
android_CloseAudioDevice(p); | |||
return NULL; | |||
} | |||
if (openSLRecOpen(p) != SL_RESULT_SUCCESS) { | |||
android_CloseAudioDevice(p); | |||
return NULL; | |||
} | |||
if (openSLPlayOpen(p) != SL_RESULT_SUCCESS) { | |||
android_CloseAudioDevice(p); | |||
return NULL; | |||
} | |||
notifyThreadLock(p->outlock); | |||
notifyThreadLock(p->inlock); | |||
p->time = 0.; | |||
return p; | |||
} | |||
// returns timestamp of the processed stream | |||
double android_GetTimestamp(OPENSL_STREAM *p) { | |||
return p->time; | |||
} | |||
// this callback handler is called every time a buffer finishes recording | |||
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { | |||
OPENSL_STREAM *p = (OPENSL_STREAM *) context; | |||
notifyThreadLock(p->inlock); | |||
} | |||
// gets a buffer of size samples from the device | |||
int android_AudioIn(OPENSL_STREAM *p, float *buffer, int size) { | |||
short *inBuffer; | |||
int i, bufsamps = p->inBufSamples, index = p->currentInputIndex; | |||
if (p == NULL || bufsamps == 0) | |||
return 0; | |||
inBuffer = p->inputBuffer[p->currentInputBuffer]; | |||
for (i = 0; i < size; i++) { | |||
if (index >= bufsamps) { | |||
waitThreadLock(p->inlock); | |||
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, inBuffer, | |||
bufsamps * sizeof(short)); | |||
p->currentInputBuffer = (p->currentInputBuffer ? 0 : 1); | |||
index = 0; | |||
inBuffer = p->inputBuffer[p->currentInputBuffer]; | |||
} | |||
buffer[i] = (float) inBuffer[index++] * CONVMYFLT; | |||
// TODO: the output buffer should be clicked | |||
//buffer[i] = min(1.f,max(-1.f,buffer[i])); | |||
} | |||
p->currentInputIndex = index; | |||
if (p->outchannels == 0) | |||
p->time += (double) size / (p->sr * p->inchannels); | |||
return i; | |||
} | |||
// this callback handler is called every time a buffer finishes playing | |||
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { | |||
OPENSL_STREAM *p = (OPENSL_STREAM *) context; | |||
notifyThreadLock(p->outlock); | |||
} | |||
// puts a buffer of size samples to the device | |||
int android_AudioOut(OPENSL_STREAM *p, float **buffer, int size) { | |||
short *outBuffer; | |||
int i, bufsamps = p->outBufSamples, index = p->currentOutputIndex; | |||
if (p == NULL || bufsamps == 0) | |||
return 0; | |||
__android_log_write(ANDROID_LOG_INFO, "FaustCPP", "Error"); | |||
outBuffer = p->outputBuffer[p->currentOutputBuffer]; | |||
if (p->outchannels == 1) { | |||
for (i = 0; i < size; i++) { | |||
outBuffer[index++] = (short) (min(1.f, max(-1.f, buffer[0][i])) | |||
* CONV16BIT); | |||
if (index >= p->outBufSamples) { | |||
waitThreadLock(p->outlock); | |||
(*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue, | |||
outBuffer, bufsamps * sizeof(short)); | |||
p->currentOutputBuffer = (p->currentOutputBuffer ? 0 : 1); | |||
index = 0; | |||
outBuffer = p->outputBuffer[p->currentOutputBuffer]; | |||
} | |||
} | |||
} else { | |||
for (i = 0; i < size; i++) { | |||
outBuffer[index++] = (short) (min(1.f, max(-1.f, buffer[0][i])) | |||
* CONV16BIT); | |||
outBuffer[index++] = (short) (min(1.f, max(-1.f, buffer[1][i])) | |||
* CONV16BIT); | |||
if (index >= p->outBufSamples) { | |||
waitThreadLock(p->outlock); | |||
(*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue, | |||
outBuffer, bufsamps * sizeof(short)); | |||
p->currentOutputBuffer = (p->currentOutputBuffer ? 0 : 1); | |||
index = 0; | |||
outBuffer = p->outputBuffer[p->currentOutputBuffer]; | |||
} | |||
} | |||
} | |||
p->currentOutputIndex = index; | |||
p->time += (double) size * p->outchannels/ (p->sr * p->outchannels); | |||
return i; | |||
} | |||
//---------------------------------------------------------------------- | |||
// thread Locks | |||
// to ensure synchronisation between callbacks and processing code | |||
void* createThreadLock(void) { | |||
threadLock *p; | |||
p = (threadLock*) malloc(sizeof(threadLock)); | |||
if (p == NULL) | |||
return NULL; | |||
memset(p, 0, sizeof(threadLock)); | |||
if (pthread_mutex_init(&(p->m), (pthread_mutexattr_t*) NULL) != 0) { | |||
free((void*) p); | |||
return NULL; | |||
} | |||
if (pthread_cond_init(&(p->c), (pthread_condattr_t*) NULL) != 0) { | |||
pthread_mutex_destroy(&(p->m)); | |||
free((void*) p); | |||
return NULL; | |||
} | |||
p->s = (unsigned char) 1; | |||
return p; | |||
} | |||
int waitThreadLock(void *lock) { | |||
threadLock *p; | |||
int retval = 0; | |||
p = (threadLock*) lock; | |||
pthread_mutex_lock(&(p->m)); | |||
while (!p->s) { | |||
pthread_cond_wait(&(p->c), &(p->m)); | |||
} | |||
p->s = (unsigned char) 0; | |||
pthread_mutex_unlock(&(p->m)); | |||
} | |||
void notifyThreadLock(void *lock) { | |||
threadLock *p; | |||
p = (threadLock*) lock; | |||
pthread_mutex_lock(&(p->m)); | |||
p->s = (unsigned char) 1; | |||
pthread_cond_signal(&(p->c)); | |||
pthread_mutex_unlock(&(p->m)); | |||
} | |||
void destroyThreadLock(void *lock) { | |||
threadLock *p; | |||
p = (threadLock*) lock; | |||
if (p == NULL) | |||
return; | |||
notifyThreadLock(p); | |||
pthread_cond_destroy(&(p->c)); | |||
pthread_mutex_destroy(&(p->m)); | |||
free(p); | |||
} |
@@ -0,0 +1,115 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __osc_dsp__ | |||
#define __osc_dsp__ | |||
#include <stdio.h> | |||
#include "faust/audio/audio.h" | |||
#include "faust/dsp/dsp.h" | |||
#include "OSCIO.h" | |||
/****************************************************************************** | |||
******************************************************************************* | |||
OSC AUDIO INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
#define kMaxBuffer 4096 | |||
class oscdsp : public audio, public oscfaust::OSCIO { | |||
dsp* fDsp; | |||
float ** fInBuffers, **fOutBuffers; | |||
public: | |||
oscdsp(const char * dst, int argc, char *argv[]) : OSCIO(dst), fDsp(0), fInBuffers(0), fOutBuffers(0) | |||
{ | |||
for (int i = 1; i < argc-1; i++) { | |||
if (!strcmp(argv[i], "-iodst")) setDest (argv[i+1]); | |||
} | |||
} | |||
virtual ~oscdsp() | |||
{ | |||
stop(); | |||
for (int i = 0; i < numInputs(); i++) { | |||
delete [] fInBuffers[i]; | |||
} | |||
for (int i = 0; i < numOutputs(); i++) { | |||
delete [] fOutBuffers[i]; | |||
} | |||
delete [] fInBuffers; | |||
delete [] fOutBuffers; | |||
} | |||
virtual bool init(const char*name, dsp* DSP) | |||
{ | |||
fDsp = DSP; | |||
fDsp->init(44100); | |||
fInBuffers = new float*[numInputs()]; | |||
fOutBuffers = new float*[numOutputs()]; | |||
for (int i = 0; i < numInputs(); i++) { | |||
fInBuffers[i] = new float[kMaxBuffer]; | |||
} | |||
for (int i = 0; i < numOutputs(); i++) { | |||
fOutBuffers [i] = new float[kMaxBuffer]; | |||
} | |||
return true; | |||
} | |||
virtual bool start() { return true; } | |||
virtual void stop() {} | |||
void compute(int nframes) | |||
{ | |||
fDsp->compute(nframes, fInBuffers, fOutBuffers); | |||
for (int i= 0; i < numOutputs(); i++) { | |||
send(nframes, fOutBuffers [i], i); | |||
} | |||
} | |||
void receive(int nvalues, float* val) | |||
{ | |||
int inChans = numInputs(); | |||
if (!inChans) { | |||
compute(nvalues); | |||
return; | |||
} | |||
for (int i=0; i < nvalues; i++) { | |||
int c = i % inChans; | |||
int frame = i / inChans; | |||
fInBuffers[c][frame] = val[i]; | |||
} | |||
compute (nvalues / inChans); | |||
} | |||
int numOutputs() const { return fDsp ? fDsp->getNumOutputs() : 0; } | |||
int numInputs() const { return fDsp ? fDsp->getNumInputs() : 0; } | |||
}; | |||
#endif |
@@ -0,0 +1,245 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __portaudio_dsp__ | |||
#define __portaudio_dsp__ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <assert.h> | |||
#include <portaudio.h> | |||
#include "faust/audio/audio.h" | |||
#include "faust/dsp/dsp-adapter.h" | |||
static bool pa_error(int err) | |||
{ | |||
if (err != paNoError) { | |||
printf("PortAudio error: %s\n", Pa_GetErrorText(err)); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
/****************************************************************************** | |||
******************************************************************************* | |||
PORT AUDIO INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
class portaudio : public audio { | |||
protected: | |||
dsp* fDsp; | |||
PaStream* fAudioStream; | |||
long fSampleRate; | |||
long fBufferSize; | |||
PaStreamParameters fInputParameters; | |||
PaStreamParameters fOutputParameters; | |||
//---------------------------------------------------------------------------- | |||
// Number of physical input and output channels of the PA device | |||
//---------------------------------------------------------------------------- | |||
int fDevNumInChans; | |||
int fDevNumOutChans; | |||
static int audioCallback(const void* ibuf, void* obuf, unsigned long frames, const PaStreamCallbackTimeInfo* time, PaStreamCallbackFlags, void* drv) | |||
{ | |||
return static_cast<portaudio*>(drv)->processAudio(time->currentTime, | |||
(float**)ibuf, | |||
static_cast<float**>(obuf), | |||
frames); | |||
} | |||
virtual int processAudio(PaTime current_time, float** ibuf, float** obuf, unsigned long frames) | |||
{ | |||
// Cleanup hardware outputs that are not used by DSP | |||
for (int i = fDsp->getNumOutputs(); i < fDevNumOutChans; i++) { | |||
memset(obuf[i], 0, sizeof(FAUSTFLOAT) * fBufferSize); | |||
} | |||
// Process samples | |||
fDsp->compute(current_time * 1000000., frames, ibuf, obuf); | |||
return paContinue; | |||
} | |||
public: | |||
portaudio(long srate, long bsize) : | |||
fDsp(0), fAudioStream(0), | |||
fSampleRate(srate), fBufferSize(bsize), | |||
fDevNumInChans(0), fDevNumOutChans(0) {} | |||
virtual ~portaudio() | |||
{ | |||
if (fAudioStream) { | |||
pa_error(Pa_StopStream(fAudioStream)); | |||
pa_error(Pa_CloseStream(fAudioStream)); | |||
fAudioStream = 0; | |||
} | |||
// Note that Pa_Initialize handled multiple times calls and | |||
// must be matched with a corresponding call to Pa_Terminate | |||
Pa_Terminate(); | |||
} | |||
virtual bool init(const char* name, dsp* DSP) | |||
{ | |||
if (init(name, DSP->getNumInputs(), DSP->getNumOutputs())) { | |||
set_dsp(DSP); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
bool init(const char* /*name*/, int numInputs, int numOutputs) | |||
{ | |||
// Note that Pa_Initialize handled multiple times calls and | |||
// must be matched with a corresponding call to Pa_Terminate | |||
if (pa_error(Pa_Initialize())) { | |||
return false; | |||
} | |||
const PaDeviceInfo* idev = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice()); | |||
const PaDeviceInfo* odev = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice()); | |||
printf("DEVICE = %p || %p\n", idev, odev); | |||
//In case there is no audio device, the function fails | |||
if (idev == NULL) { | |||
fDevNumInChans = 0; | |||
} else { | |||
fDevNumInChans = idev->maxInputChannels; | |||
fInputParameters.device = Pa_GetDefaultInputDevice(); | |||
fInputParameters.sampleFormat = paFloat32 | paNonInterleaved; | |||
fInputParameters.channelCount = fDevNumInChans; | |||
fInputParameters.hostApiSpecificStreamInfo = 0; | |||
} | |||
if (odev == NULL) { | |||
fDevNumOutChans = 0; | |||
} else{ | |||
fDevNumOutChans = odev->maxOutputChannels; | |||
fOutputParameters.device = Pa_GetDefaultOutputDevice(); | |||
fOutputParameters.sampleFormat = paFloat32 | paNonInterleaved;; | |||
fOutputParameters.channelCount = fDevNumOutChans; | |||
fOutputParameters.hostApiSpecificStreamInfo = 0; | |||
} | |||
// A DSP that has only outputs or only inputs forces the presence of an output or input device | |||
if (numInputs == 0 && numOutputs != 0 && fDevNumOutChans == 0) { | |||
printf("Devices not adapted to DSP\n"); | |||
return false; | |||
} | |||
if (numInputs != 0 && numOutputs == 0 && fDevNumInChans == 0) { | |||
printf("Devices not adapted to DSP\n"); | |||
return false; | |||
} | |||
// If no device exists : the function fails | |||
PaError err; | |||
if ((err = Pa_IsFormatSupported(((fDevNumInChans > 0) ? &fInputParameters : 0), | |||
((fDevNumOutChans > 0) ? &fOutputParameters : 0), fSampleRate)) != 0) { | |||
printf("stream format is not supported err = %d\n", err); | |||
return false; | |||
} | |||
if (pa_error(Pa_OpenStream(&fAudioStream, ((fDevNumInChans > 0) ? &fInputParameters : 0), | |||
((fDevNumOutChans > 0) ? &fOutputParameters : 0), | |||
fSampleRate, fBufferSize, paNoFlag, audioCallback, this))) { | |||
return false; | |||
} | |||
return true; | |||
} | |||
void set_dsp(dsp* DSP) | |||
{ | |||
fDsp = DSP; | |||
if (fDsp->getNumInputs() > fDevNumInChans || fDsp->getNumOutputs() > fDevNumOutChans) { | |||
printf("DSP has %d inputs and %d outputs, physical inputs = %d physical outputs = %d \n", | |||
fDsp->getNumInputs(), fDsp->getNumOutputs(), | |||
fDevNumInChans, fDevNumOutChans); | |||
fDsp = new dsp_adapter(fDsp, fDevNumInChans, fDevNumOutChans, fBufferSize); | |||
} | |||
fDsp->init(fSampleRate); | |||
} | |||
virtual bool start() | |||
{ | |||
if (pa_error(Pa_StartStream(fAudioStream))) { | |||
return false; | |||
} else { | |||
return true; | |||
} | |||
} | |||
virtual void stop() | |||
{ | |||
if (fAudioStream) { | |||
pa_error(Pa_StopStream(fAudioStream)); | |||
} | |||
} | |||
virtual int get_buffer_size() | |||
{ | |||
return fBufferSize; | |||
} | |||
virtual int get_sample_rate() | |||
{ | |||
return fSampleRate; | |||
} | |||
virtual int get_num_inputs() | |||
{ | |||
return fDevNumInChans; | |||
} | |||
virtual int get_num_outputs() | |||
{ | |||
return fDevNumOutChans; | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,215 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __rtaudio_dsp__ | |||
#define __rtaudio_dsp__ | |||
#include <stdio.h> | |||
#include <assert.h> | |||
#include <RtAudio.h> | |||
#include <stdlib.h> | |||
#include "faust/audio/audio.h" | |||
#include "faust/dsp/dsp-adapter.h" | |||
#define FORMAT RTAUDIO_FLOAT32 | |||
/****************************************************************************** | |||
******************************************************************************* | |||
RTAUDIO INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
class rtaudio : public audio { | |||
protected: | |||
dsp* fDsp; | |||
RtAudio fAudioDAC; | |||
unsigned int fSampleRate; | |||
unsigned int fBufferSize; | |||
//---------------------------------------------------------------------------- | |||
// number of physical input and output channels of the PA device | |||
//---------------------------------------------------------------------------- | |||
int fDevNumInChans; | |||
int fDevNumOutChans; | |||
virtual int processAudio(double streamTime, void* inbuf, void* outbuf, unsigned long frames) | |||
{ | |||
float* inputs[fDsp->getNumInputs()]; | |||
float* outputs[fDsp->getNumOutputs()]; | |||
for (int i = 0; i < fDsp->getNumInputs(); i++) { | |||
inputs[i] = &(static_cast<float*>(inbuf))[i * frames]; | |||
} | |||
for (int i = 0; i < fDsp->getNumOutputs(); i++) { | |||
outputs[i] = &(static_cast<float*>(outbuf))[i * frames]; | |||
} | |||
// process samples | |||
fDsp->compute(streamTime * 1000000., frames, inputs, outputs); | |||
return 0; | |||
} | |||
static int audioCallback(void* outputBuffer, void* inputBuffer, | |||
unsigned int nBufferFrames, | |||
double streamTime, RtAudioStreamStatus status, | |||
void* drv) | |||
{ | |||
return static_cast<rtaudio*>(drv)->processAudio(streamTime, inputBuffer, outputBuffer, nBufferFrames); | |||
} | |||
public: | |||
rtaudio(int srate, int bsize) : fDsp(0), | |||
fSampleRate(srate), fBufferSize(bsize), | |||
fDevNumInChans(0), fDevNumOutChans(0) {} | |||
virtual ~rtaudio() | |||
{ | |||
try { | |||
fAudioDAC.stopStream(); | |||
fAudioDAC.closeStream(); | |||
} catch (RtAudioError& e) { | |||
std::cout << '\n' << e.getMessage() << '\n' << std::endl; | |||
} | |||
} | |||
virtual bool init(const char* name, dsp* DSP) | |||
{ | |||
if (init(name, DSP->getNumInputs(), DSP->getNumOutputs())) { | |||
set_dsp(DSP); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
bool init(const char* /*name*/, int numInputs, int numOutputs) | |||
{ | |||
if (fAudioDAC.getDeviceCount() < 1) { | |||
std::cout << "No audio devices found!\n"; | |||
return false; | |||
} | |||
RtAudio::DeviceInfo info_in = fAudioDAC.getDeviceInfo(fAudioDAC.getDefaultInputDevice()); | |||
RtAudio::DeviceInfo info_out = fAudioDAC.getDeviceInfo(fAudioDAC.getDefaultOutputDevice()); | |||
RtAudio::StreamParameters iParams, oParams; | |||
iParams.deviceId = fAudioDAC.getDefaultInputDevice(); | |||
fDevNumInChans = info_in.inputChannels; | |||
iParams.nChannels = fDevNumInChans; | |||
iParams.firstChannel = 0; | |||
oParams.deviceId = fAudioDAC.getDefaultOutputDevice(); | |||
fDevNumOutChans = info_out.outputChannels; | |||
oParams.nChannels = fDevNumOutChans; | |||
oParams.firstChannel = 0; | |||
RtAudio::StreamOptions options; | |||
options.flags |= RTAUDIO_NONINTERLEAVED; | |||
try { | |||
fAudioDAC.openStream(((numOutputs > 0) ? &oParams : NULL), | |||
((numInputs > 0) ? &iParams : NULL), FORMAT, | |||
fSampleRate, &fBufferSize, audioCallback, this, &options); | |||
} catch (RtAudioError& e) { | |||
std::cout << '\n' << e.getMessage() << '\n' << std::endl; | |||
return false; | |||
} | |||
return true; | |||
} | |||
void set_dsp(dsp* DSP) | |||
{ | |||
fDsp = DSP; | |||
if (fDsp->getNumInputs() > fDevNumInChans || fDsp->getNumOutputs() > fDevNumOutChans) { | |||
printf("DSP has %d inputs and %d outputs, physical inputs = %d physical outputs = %d \n", | |||
fDsp->getNumInputs(), fDsp->getNumOutputs(), | |||
fDevNumInChans, fDevNumOutChans); | |||
fDsp = new dsp_adapter(fDsp, fDevNumInChans, fDevNumOutChans, fBufferSize); | |||
} | |||
fDsp->init(fSampleRate); | |||
} | |||
virtual bool start() | |||
{ | |||
try { | |||
fAudioDAC.startStream(); | |||
} catch (RtAudioError& e) { | |||
std::cout << '\n' << e.getMessage() << '\n' << std::endl; | |||
return false; | |||
} | |||
return true; | |||
} | |||
virtual void stop() | |||
{ | |||
try { | |||
fAudioDAC.stopStream(); | |||
} catch (RtAudioError& e) { | |||
std::cout << '\n' << e.getMessage() << '\n' << std::endl; | |||
} | |||
} | |||
virtual int get_buffer_size() | |||
{ | |||
return fBufferSize; | |||
} | |||
virtual int get_sample_rate() | |||
{ | |||
return fSampleRate; | |||
} | |||
virtual int get_num_inputs() | |||
{ | |||
return fDevNumInChans; | |||
} | |||
virtual int get_num_outputs() | |||
{ | |||
return fDevNumOutChans; | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,109 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __dsp_adapter__ | |||
#define __dsp_adapter__ | |||
#include <string.h> | |||
#include "faust/dsp/dsp.h" | |||
class dsp_adapter : public decorator_dsp { | |||
private: | |||
FAUSTFLOAT** fAdaptedInputs; | |||
FAUSTFLOAT** fAdaptedOutputs; | |||
int fHardwareInputs; | |||
int fHardwareOutputs; | |||
void adaptBuffers(FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
for (int i = 0; i < fHardwareInputs; i++) { | |||
fAdaptedInputs[i] = inputs[i]; | |||
} | |||
for (int i = 0; i < fHardwareOutputs; i++) { | |||
fAdaptedOutputs[i] = outputs[i]; | |||
} | |||
} | |||
public: | |||
dsp_adapter(dsp* dsp, int hardware_inputs, int hardware_outputs, int buffer_size):decorator_dsp(dsp) | |||
{ | |||
fHardwareInputs = hardware_inputs; | |||
fHardwareOutputs = hardware_outputs; | |||
fAdaptedInputs = new FAUSTFLOAT*[dsp->getNumInputs()]; | |||
for (int i = 0; i < dsp->getNumInputs() - fHardwareInputs; i++) { | |||
fAdaptedInputs[i + fHardwareInputs] = new FAUSTFLOAT[buffer_size]; | |||
memset(fAdaptedInputs[i + fHardwareInputs], 0, sizeof(FAUSTFLOAT) * buffer_size); | |||
} | |||
fAdaptedOutputs = new FAUSTFLOAT*[dsp->getNumOutputs()]; | |||
for (int i = 0; i < dsp->getNumOutputs() - fHardwareOutputs; i++) { | |||
fAdaptedOutputs[i + fHardwareOutputs] = new FAUSTFLOAT[buffer_size]; | |||
memset(fAdaptedOutputs[i + fHardwareOutputs], 0, sizeof(FAUSTFLOAT) * buffer_size); | |||
} | |||
} | |||
virtual ~dsp_adapter() | |||
{ | |||
for (int i = 0; i < fDSP->getNumInputs() - fHardwareInputs; i++) { | |||
delete [] fAdaptedInputs[i + fHardwareInputs]; | |||
} | |||
delete [] fAdaptedInputs; | |||
for (int i = 0; i < fDSP->getNumOutputs() - fHardwareOutputs; i++) { | |||
delete [] fAdaptedOutputs[i + fHardwareOutputs]; | |||
} | |||
delete [] fAdaptedOutputs; | |||
} | |||
virtual int getNumInputs() { return fHardwareInputs; } | |||
virtual int getNumOutputs() { return fHardwareOutputs; } | |||
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
adaptBuffers(inputs, outputs); | |||
fDSP->compute(date_usec, count, fAdaptedInputs, fAdaptedOutputs); | |||
} | |||
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
adaptBuffers(inputs, outputs); | |||
fDSP->compute(count, fAdaptedInputs, fAdaptedOutputs); | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,382 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************/ | |||
#ifndef __dsp_bench__ | |||
#define __dsp_bench__ | |||
#include <limits.h> | |||
#include <sys/time.h> | |||
#include <iostream> | |||
#include <fstream> | |||
#include <vector> | |||
#include <algorithm> | |||
#include <assert.h> | |||
#include <string.h> | |||
#include "faust/dsp/dsp.h" | |||
// Handle 32/64 bits int size issues | |||
#ifdef __x86_64__ | |||
#define uint32 unsigned int | |||
#define uint64 unsigned long int | |||
#define int32 int | |||
#define int64 long int | |||
#else | |||
#define uint32 unsigned int | |||
#define uint64 unsigned long long int | |||
#define int32 int | |||
#define int64 long long int | |||
#endif | |||
/* | |||
A class to do do timing measurements | |||
*/ | |||
class time_bench { | |||
protected: | |||
int fMeasure; | |||
int fMeasureCount; | |||
int fSkip; | |||
// These values are used to determine the number of clocks in a second | |||
uint64 fFirstRDTSC; | |||
uint64 fLastRDTSC; | |||
// These tables contains the last fMeasureCount in clocks | |||
uint64* fStarts; | |||
uint64* fStops; | |||
struct timeval fTv1; | |||
struct timeval fTv2; | |||
/** | |||
* Returns the number of clock cycles elapsed since the last reset of the processor | |||
*/ | |||
inline uint64 rdtsc(void) | |||
{ | |||
union { | |||
uint32 i32[2]; | |||
uint64 i64; | |||
} count; | |||
__asm__ __volatile__("rdtsc" : "=a" (count.i32[0]), "=d" (count.i32[1])); | |||
return count.i64; | |||
} | |||
/** | |||
* return the number of RDTSC clocks per seconds | |||
*/ | |||
double rdtscpersec() | |||
{ | |||
// If the environment variable CLOCKSPERSEC is defined | |||
// we use it instead of our own measurement | |||
char* str = getenv("CLOCKSPERSEC"); | |||
if (str) { | |||
int64 cps = (int64)atoll(str); | |||
if (cps > 1000000000) { | |||
return cps; | |||
} | |||
} | |||
return double(fLastRDTSC - fFirstRDTSC) | |||
/ (((double(fTv2.tv_sec) * 1000000 + double(fTv2.tv_usec)) - (double(fTv1.tv_sec) * 1000000 + double(fTv1.tv_usec))) | |||
/ 1000000); | |||
} | |||
/** | |||
* Converts a duration, expressed in RDTSC clocks, into seconds | |||
*/ | |||
double rdtsc2sec(uint64 clk) | |||
{ | |||
return double(clk) / rdtscpersec(); | |||
} | |||
double rdtsc2sec(double clk) | |||
{ | |||
return clk / rdtscpersec(); | |||
} | |||
/** | |||
* Converts RDTSC clocks into Megabytes/seconds according to the | |||
* number of frames processed during the period, the number of channels | |||
* and 4 bytes samples. | |||
*/ | |||
double megapersec(int frames, int chans, uint64 clk) | |||
{ | |||
return double(frames) * double(chans) * 4 / double(1024 * 1024 * rdtsc2sec(clk)); | |||
} | |||
/** | |||
* Compute the mean value of a vector of measures | |||
*/ | |||
uint64 meanValue(std::vector<uint64>::const_iterator a, std::vector<uint64>::const_iterator b) | |||
{ | |||
uint64 r = 0; | |||
unsigned int n = 0; | |||
while (a != b) { r += *a++; n++; } | |||
return (n > 0) ? r/n : 0; | |||
} | |||
public: | |||
time_bench(int count, int skip) | |||
{ | |||
fSkip = skip; | |||
fMeasureCount = count; | |||
fMeasure = 0; | |||
fFirstRDTSC = 0; | |||
fLastRDTSC = 0; | |||
fStarts = new uint64[fMeasureCount]; | |||
fStops = new uint64[fMeasureCount]; | |||
} | |||
virtual ~time_bench() | |||
{ | |||
delete [] fStarts; | |||
delete [] fStops; | |||
} | |||
void startMeasure() { fStarts[fMeasure % fMeasureCount] = rdtsc(); } | |||
void stopMeasure() { fStops[fMeasure % fMeasureCount] = rdtsc(); fMeasure++; } | |||
void openMeasure() | |||
{ | |||
struct timezone tz; | |||
gettimeofday(&fTv1, &tz); | |||
fFirstRDTSC = rdtsc(); | |||
fMeasure = 0; | |||
} | |||
void closeMeasure() | |||
{ | |||
struct timezone tz; | |||
gettimeofday(&fTv2, &tz); | |||
fLastRDTSC = rdtsc(); | |||
} | |||
double measureDurationUsec() | |||
{ | |||
return ((double(fTv2.tv_sec) * 1000000 + double(fTv2.tv_usec)) - (double(fTv1.tv_sec) * 1000000 + double(fTv1.tv_usec))); | |||
} | |||
/** | |||
* Returns best estimation. | |||
*/ | |||
double getStats(int bsize, int ichans, int ochans) | |||
{ | |||
//std::cout << "getStats fMeasure = " << fMeasure << " fMeasureCount = " << fMeasureCount << std::endl; | |||
assert(fMeasure > fMeasureCount); | |||
std::vector<uint64> V(fMeasureCount); | |||
for (int i = 0; i < fMeasureCount; i++) { | |||
V[i] = fStops[i] - fStarts[i]; | |||
} | |||
sort(V.begin(), V.end()); | |||
// Mean of 10 best values (gives relatively stable results) | |||
uint64 meavalx = meanValue(V.begin(), V.begin() + 10); | |||
return megapersec(bsize, ichans + ochans, meavalx); | |||
} | |||
/** | |||
* Print the median value (in Megabytes/second) of fMeasureCount throughputs measurements. | |||
*/ | |||
void printStats(const char* applname, int bsize, int ichans, int ochans) | |||
{ | |||
assert(fMeasure > fMeasureCount); | |||
std::vector<uint64> V(fMeasureCount); | |||
for (int i = 0; i < fMeasureCount; i++) { | |||
V[i] = fStops[i] - fStarts[i]; | |||
} | |||
sort(V.begin(), V.end()); | |||
// Mean of 10 best values (gives relatively stable results) | |||
uint64 meaval00 = meanValue(V.begin(), V.begin()+ 5); | |||
uint64 meaval25 = meanValue(V.begin() + fMeasureCount / 4 - 2, V.begin()+fMeasureCount / 4 + 3); | |||
uint64 meaval50 = meanValue(V.begin() + fMeasureCount / 2 - 2, V.begin()+fMeasureCount / 2 + 3); | |||
uint64 meaval75 = meanValue(V.begin() + 3 * fMeasureCount / 4 - 2, V.begin() + 3 * fMeasureCount / 4 + 3); | |||
uint64 meaval100 = meanValue(V.end() - 5, V.end()); | |||
// Printing | |||
std::cout << applname | |||
<< '\t' << megapersec(bsize, ichans+ochans, meaval00) | |||
<< '\t' << megapersec(bsize, ichans+ochans, meaval25) | |||
<< '\t' << megapersec(bsize, ichans+ochans, meaval50) | |||
<< '\t' << megapersec(bsize, ichans+ochans, meaval75) | |||
<< '\t' << megapersec(bsize, ichans+ochans, meaval100) | |||
<< std::endl; | |||
} | |||
bool isRunning() { return (fMeasure <= (fMeasureCount + fSkip)); } | |||
}; | |||
/* | |||
A class to measure DSP CPU use. | |||
*/ | |||
class measure_dsp : public decorator_dsp { | |||
protected: | |||
FAUSTFLOAT** fInputs; | |||
FAUSTFLOAT** fOutputs; | |||
time_bench* fBench; | |||
int fBufferSize; | |||
void init() | |||
{ | |||
fInputs = new FAUSTFLOAT*[fDSP->getNumInputs()]; | |||
for (int i = 0; i < fDSP->getNumInputs(); i++) { | |||
fInputs[i] = new FAUSTFLOAT[fBufferSize]; | |||
memset(fInputs[i], 0, sizeof(FAUSTFLOAT) * fBufferSize); | |||
} | |||
fOutputs = new FAUSTFLOAT*[fDSP->getNumOutputs()]; | |||
for (int i = 0; i < fDSP->getNumOutputs(); i++) { | |||
fOutputs[i] = new FAUSTFLOAT[fBufferSize]; | |||
memset(fOutputs[i], 0, sizeof(FAUSTFLOAT) * fBufferSize); | |||
} | |||
} | |||
public: | |||
/** | |||
* Constructor. | |||
* | |||
* @param dsp - the dsp to be measured. | |||
* @param buffer_size - the buffer size used when calling 'computeAll' | |||
* @param count - the number of cycles using in 'computeAll' | |||
* @param skip - ?? | |||
* | |||
*/ | |||
measure_dsp(dsp* dsp, int buffer_size, int count, int skip) | |||
:decorator_dsp(dsp), fBufferSize(buffer_size) | |||
{ | |||
init(); | |||
fBench = new time_bench(count, 10); | |||
} | |||
measure_dsp(dsp* dsp, int buffer_size, double duration_in_sec) | |||
:decorator_dsp(dsp), fBufferSize(buffer_size) | |||
{ | |||
init(); | |||
fBench = new time_bench(500, 10); | |||
measure(); | |||
double duration = fBench->measureDurationUsec(); | |||
int cout = int (500 * (duration_in_sec * 1e6 / duration)); | |||
std::cout << "duration = " << duration << " count = " << cout << std::endl; | |||
delete fBench; | |||
fBench = new time_bench(cout, 10); | |||
} | |||
virtual ~measure_dsp() | |||
{ | |||
for (int i = 0; i < fDSP->getNumInputs(); i++) { | |||
delete [] fInputs[i]; | |||
} | |||
delete [] fInputs; | |||
for (int i = 0; i < fDSP->getNumOutputs(); i++) { | |||
delete [] fOutputs[i]; | |||
} | |||
delete[] fOutputs; | |||
} | |||
/* | |||
Measure the duration of the compute call | |||
*/ | |||
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
fBench->startMeasure(); | |||
fDSP->compute(count, inputs, outputs); | |||
fBench->stopMeasure(); | |||
} | |||
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
compute(count, inputs, outputs); | |||
} | |||
/* | |||
Measure the duration of 'count' (given in constructor) calls to compute | |||
*/ | |||
void computeAll() | |||
{ | |||
AVOIDDENORMALS; | |||
do { | |||
compute(0, fBufferSize, fInputs, fOutputs); | |||
} while (fBench->isRunning()); | |||
} | |||
/** | |||
* Initialize measure datas | |||
*/ | |||
void openMeasure() { fBench->openMeasure(); } | |||
/** | |||
* Terminate measurement | |||
*/ | |||
void closeMeasure() { fBench->closeMeasure(); } | |||
double measureDurationUsec() | |||
{ | |||
return fBench->measureDurationUsec(); | |||
} | |||
void measure() | |||
{ | |||
openMeasure(); | |||
computeAll(); | |||
closeMeasure(); | |||
} | |||
/** | |||
* Returns best estimation | |||
*/ | |||
double getStats() | |||
{ | |||
return fBench->getStats(fBufferSize, fDSP->getNumInputs(), fDSP->getNumOutputs()); | |||
} | |||
/** | |||
* Print the median value (in Megabytes/second) of fMeasureCount throughputs measurements | |||
*/ | |||
void printStats(const char* applname) | |||
{ | |||
fBench->printStats(applname, fBufferSize, fDSP->getNumInputs(), fDSP->getNumOutputs()); | |||
} | |||
bool isRunning() { return fBench->isRunning(); } | |||
}; | |||
#endif | |||
@@ -0,0 +1,246 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __dsp_combiner__ | |||
#define __dsp_combiner__ | |||
#include <string.h> | |||
#include <assert.h> | |||
#include "faust/dsp/dsp.h" | |||
// Combine two DSP in sequence | |||
class dsp_sequencer : public dsp { | |||
private: | |||
dsp* fDSP1; | |||
dsp* fDSP2; | |||
FAUSTFLOAT** fSeqBuffer; | |||
public: | |||
dsp_sequencer(dsp* dsp1, dsp* dsp2, int buffer_size = 4096) | |||
:fDSP1(dsp1), fDSP2(dsp2) | |||
{ | |||
assert(fDSP1->getNumOutputs() == fDSP2->getNumInputs()); | |||
fSeqBuffer = new FAUSTFLOAT*[fDSP1->getNumOutputs()]; | |||
for (int i = 0; i < fDSP1->getNumOutputs(); i++) { | |||
fSeqBuffer[i] = new FAUSTFLOAT[buffer_size]; | |||
} | |||
} | |||
virtual ~dsp_sequencer() | |||
{ | |||
for (int i = 0; i < fDSP1->getNumOutputs(); i++) { | |||
delete [] fSeqBuffer[i]; | |||
} | |||
delete [] fSeqBuffer; | |||
delete fDSP1; | |||
delete fDSP2; | |||
} | |||
virtual int getNumInputs() { return fDSP1->getNumInputs(); } | |||
virtual int getNumOutputs() { return fDSP2->getNumOutputs(); } | |||
virtual void buildUserInterface(UI* ui_interface) | |||
{ | |||
ui_interface->openTabBox("Sequencer"); | |||
ui_interface->openVerticalBox("DSP1"); | |||
fDSP1->buildUserInterface(ui_interface); | |||
ui_interface->closeBox(); | |||
ui_interface->openVerticalBox("DSP2"); | |||
fDSP2->buildUserInterface(ui_interface); | |||
ui_interface->closeBox(); | |||
ui_interface->closeBox(); | |||
} | |||
virtual int getSampleRate() | |||
{ | |||
return fDSP1->getSampleRate(); | |||
} | |||
virtual void init(int samplingRate) | |||
{ | |||
fDSP1->init(samplingRate); | |||
fDSP2->init(samplingRate); | |||
} | |||
virtual void instanceInit(int samplingRate) | |||
{ | |||
fDSP1->instanceInit(samplingRate); | |||
fDSP2->instanceInit(samplingRate); | |||
} | |||
virtual void instanceConstants(int samplingRate) | |||
{ | |||
fDSP1->instanceConstants(samplingRate); | |||
fDSP2->instanceConstants(samplingRate); | |||
} | |||
virtual void instanceResetUserInterface() | |||
{ | |||
fDSP1->instanceResetUserInterface(); | |||
fDSP2->instanceResetUserInterface(); | |||
} | |||
virtual void instanceClear() | |||
{ | |||
fDSP1->instanceClear(); | |||
fDSP2->instanceClear(); | |||
} | |||
virtual dsp* clone() | |||
{ | |||
return new dsp_sequencer(fDSP1->clone(), fDSP2->clone()); | |||
} | |||
virtual void metadata(Meta* m) | |||
{ | |||
fDSP1->metadata(m); | |||
fDSP2->metadata(m); | |||
} | |||
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
fDSP1->compute(count, inputs, fSeqBuffer); | |||
fDSP2->compute(count, fSeqBuffer, outputs); | |||
} | |||
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } | |||
}; | |||
// Combine two DSP in parallel | |||
class dsp_parallelizer : public dsp { | |||
private: | |||
dsp* fDSP1; | |||
dsp* fDSP2; | |||
public: | |||
dsp_parallelizer(dsp* dsp1, dsp* dsp2, int buffer_size = 4096) | |||
:fDSP1(dsp1), fDSP2(dsp2) | |||
{} | |||
virtual ~dsp_parallelizer() | |||
{ | |||
delete fDSP1; | |||
delete fDSP2; | |||
} | |||
virtual int getNumInputs() { return fDSP1->getNumInputs() + fDSP2->getNumInputs(); } | |||
virtual int getNumOutputs() { return fDSP1->getNumOutputs() + fDSP2->getNumOutputs(); } | |||
virtual void buildUserInterface(UI* ui_interface) | |||
{ | |||
ui_interface->openTabBox("Parallelizer"); | |||
ui_interface->openVerticalBox("DSP1"); | |||
fDSP1->buildUserInterface(ui_interface); | |||
ui_interface->closeBox(); | |||
ui_interface->openVerticalBox("DSP2"); | |||
fDSP2->buildUserInterface(ui_interface); | |||
ui_interface->closeBox(); | |||
ui_interface->closeBox(); | |||
} | |||
virtual int getSampleRate() | |||
{ | |||
return fDSP1->getSampleRate(); | |||
} | |||
virtual void init(int samplingRate) | |||
{ | |||
fDSP1->init(samplingRate); | |||
fDSP2->init(samplingRate); | |||
} | |||
virtual void instanceInit(int samplingRate) | |||
{ | |||
fDSP1->instanceInit(samplingRate); | |||
fDSP2->instanceInit(samplingRate); | |||
} | |||
virtual void instanceConstants(int samplingRate) | |||
{ | |||
fDSP1->instanceConstants(samplingRate); | |||
fDSP2->instanceConstants(samplingRate); | |||
} | |||
virtual void instanceResetUserInterface() | |||
{ | |||
fDSP1->instanceResetUserInterface(); | |||
fDSP2->instanceResetUserInterface(); | |||
} | |||
virtual void instanceClear() | |||
{ | |||
fDSP1->instanceClear(); | |||
fDSP2->instanceClear(); | |||
} | |||
virtual dsp* clone() | |||
{ | |||
return new dsp_parallelizer(fDSP1->clone(), fDSP2->clone()); | |||
} | |||
virtual void metadata(Meta* m) | |||
{ | |||
fDSP1->metadata(m); | |||
fDSP2->metadata(m); | |||
} | |||
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
fDSP1->compute(count, inputs, outputs); | |||
// Shift inputs/outputs channels for fDSP2 | |||
FAUSTFLOAT** inputs_dsp2 = (FAUSTFLOAT**)alloca(fDSP2->getNumInputs() * sizeof(FAUSTFLOAT*)); | |||
for (int chan = 0; chan < fDSP2->getNumInputs(); chan++) { | |||
inputs_dsp2[chan] = inputs[fDSP1->getNumInputs() + chan]; | |||
} | |||
FAUSTFLOAT** outputs_dsp2 = (FAUSTFLOAT**)alloca(fDSP2->getNumOutputs() * sizeof(FAUSTFLOAT*)); | |||
for (int chan = 0; chan < fDSP2->getNumOutputs(); chan++) { | |||
outputs_dsp2[chan] = inputs[fDSP1->getNumOutputs() + chan]; | |||
} | |||
fDSP2->compute(count, inputs_dsp2, outputs_dsp2); | |||
} | |||
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } | |||
}; | |||
#endif |
@@ -0,0 +1,191 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
/****************************************************************************** | |||
******************************************************************************* | |||
FAUST DSP | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
#ifndef __dsp__ | |||
#define __dsp__ | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
class UI; | |||
struct Meta; | |||
/** | |||
* Signal processor definition. | |||
*/ | |||
class dsp { | |||
public: | |||
dsp() {} | |||
virtual ~dsp() {} | |||
/* Return instance number of audio inputs */ | |||
virtual int getNumInputs() = 0; | |||
/* Return instance number of audio outputs */ | |||
virtual int getNumOutputs() = 0; | |||
/** | |||
* Trigger the UI* parameter with instance specific calls | |||
* to 'addBtton', 'addVerticalSlider'... in order to build the UI. | |||
* | |||
* @param ui_interface - the UI* user interface builder | |||
*/ | |||
virtual void buildUserInterface(UI* ui_interface) = 0; | |||
/* Returns the sample rate currently used by the instance */ | |||
virtual int getSampleRate() = 0; | |||
/** Global init, calls the following methods : | |||
* - static class 'classInit' : static table initialisation | |||
* - 'instanceInit' : constants and instance table initialisation | |||
* | |||
* @param samplingRate - the sampling rate in Herz | |||
*/ | |||
virtual void init(int samplingRate) = 0; | |||
/** Init instance state | |||
* | |||
* @param samplingRate - the sampling rate in Herz | |||
*/ | |||
virtual void instanceInit(int samplingRate) = 0; | |||
/** Init instance constant state | |||
* | |||
* @param samplingRate - the sampling rate in Herz | |||
*/ | |||
virtual void instanceConstants(int samplingRate) = 0; | |||
/* Init default control parameters values */ | |||
virtual void instanceResetUserInterface() = 0; | |||
/* Init instance state (delay lines...) */ | |||
virtual void instanceClear() = 0; | |||
/** | |||
* Return a clone of the instance. | |||
* | |||
* @return a copy of the instance on success, otherwise a null pointer. | |||
*/ | |||
virtual dsp* clone() = 0; | |||
/** | |||
* Trigger the Meta* parameter with instance specific calls to 'declare' (key, value metadata). | |||
* | |||
* @param m - the Meta* meta user | |||
*/ | |||
virtual void metadata(Meta* m) = 0; | |||
/** | |||
* DSP instance computation, to be called with sucessive in/out audio buffers. | |||
* | |||
* @param count - the nomber of frames to compute | |||
* @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, doucbe or quad) | |||
* @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, doucbe or quad) | |||
* | |||
*/ | |||
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; | |||
/** | |||
* DSP instance computation : alternative method to be used by subclasses. | |||
* | |||
* @param date_usec - the timestamp in microsec given by audio driver. | |||
* @param count - the nomber of frames to compute | |||
* @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, doucbe or quad) | |||
* @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, doucbe or quad) | |||
* | |||
*/ | |||
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } | |||
}; | |||
/** | |||
* Generic DSP decorator. | |||
*/ | |||
class decorator_dsp : public ::dsp { | |||
protected: | |||
dsp* fDSP; | |||
public: | |||
decorator_dsp(dsp* dsp = 0):fDSP(dsp) {} | |||
virtual ~decorator_dsp() { delete fDSP; } | |||
virtual int getNumInputs() { return fDSP->getNumInputs(); } | |||
virtual int getNumOutputs() { return fDSP->getNumOutputs(); } | |||
virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); } | |||
virtual int getSampleRate() { return fDSP->getSampleRate(); } | |||
virtual void init(int samplingRate) { fDSP->init(samplingRate); } | |||
virtual void instanceInit(int samplingRate) { fDSP->instanceInit(samplingRate); } | |||
virtual void instanceConstants(int samplingRate) { fDSP->instanceConstants(samplingRate); } | |||
virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); } | |||
virtual void instanceClear() { fDSP->instanceClear(); } | |||
virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); } | |||
virtual void metadata(Meta* m) { return fDSP->metadata(m); } | |||
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); } | |||
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); } | |||
}; | |||
/** | |||
* On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero) | |||
* flags to avoid costly denormals. | |||
*/ | |||
#ifdef __SSE__ | |||
#include <xmmintrin.h> | |||
#ifdef __SSE2__ | |||
#define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040) | |||
#else | |||
#define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000) | |||
#endif | |||
#else | |||
#define AVOIDDENORMALS | |||
#endif | |||
#endif |
@@ -0,0 +1,68 @@ | |||
/************************************************************************ | |||
************************************************************************ | |||
FAUST Polyphonic Architecture File | |||
Copyright (C) 2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This is sample code. This file is provided as an example of minimal | |||
FAUST architecture file. Redistribution and use in source and binary | |||
forms, with or without modification, in part or in full are permitted. | |||
In particular you can create a derived work of this FAUST architecture | |||
and distribute that work under terms of your choice. | |||
This sample code 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. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __faust_engine__ | |||
#define __faust_engine__ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
void* create(int, int); // To be implemented | |||
void destroy(void*); | |||
bool start(void*); | |||
void stop(void*); | |||
bool isRunning(void*); | |||
long keyOn(void*, int, int); | |||
int keyOff(void*, int); | |||
void propagateMidi(void*, int, double, int, int, int, int); | |||
const char* getJSONUI(void*); | |||
const char* getJSONMeta(void*); | |||
int getParamsCount(void*); | |||
void setParamValue(void*, const char*, float); | |||
float getParamValue(void*, const char*); | |||
void setParamIdValue(void*, int, float); | |||
float getParamIdValue(void*, int); | |||
void setVoiceParamValue(void*, const char*, long, float); | |||
float getVoiceParamValue(void*, const char*, long); | |||
const char* getParamAddress(void*, int); | |||
void propagateAcc(void*, int, float); | |||
void setAccConverter(void*, int, int, int, float, float, float); | |||
void propagateGyr(void*, int, float); | |||
void setGyrConverter(void*, int, int, int, float, float, float); | |||
float getCPULoad(void*); | |||
int getScreenColor(void*); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif // __faust_engine__ |
@@ -0,0 +1,627 @@ | |||
/************************************************************************ | |||
************************************************************************ | |||
FAUST Polyphonic Architecture File | |||
Copyright (C) 2013 GRAME, Romain Michon, CCRMA - Stanford University | |||
Copyright (C) 2003-2015 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This is sample code. This file is provided as an example of minimal | |||
FAUST architecture file. Redistribution and use in source and binary | |||
forms, with or without modification, in part or in full are permitted. | |||
In particular you can create a derived work of this FAUST architecture | |||
and distribute that work under terms of your choice. | |||
This sample code 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. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __faust_poly_engine__ | |||
#define __faust_poly_engine__ | |||
#include <math.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include "faust/misc.h" | |||
#include "faust/dsp/dsp.h" | |||
#include "faust/audio/audio.h" | |||
#include "faust/gui/meta.h" | |||
#include "faust/gui/JSONUI.h" | |||
#include "faust/gui/APIUI.h" | |||
#include "faust/dsp/poly-dsp.h" | |||
#include "faust/dsp/faust-engine.h" | |||
#include "faust/dsp/dsp-combiner.h" | |||
//************************************************************** | |||
// Mono or polyphonic audio DSP engine | |||
//************************************************************** | |||
using namespace std; | |||
struct MyMeta : public Meta, public std::map<const char*, const char*> | |||
{ | |||
void declare(const char* key, const char* value) | |||
{ | |||
(*this)[key] = value; | |||
} | |||
const char* get(const char* key, const char* def) | |||
{ | |||
if (this->find(key) != this->end()) { | |||
return (*this)[key]; | |||
} else { | |||
return def; | |||
} | |||
} | |||
}; | |||
static void analyseMeta(bool& midi_sync, int& nvoices) | |||
{ | |||
mydsp* tmp_dsp = new mydsp(); | |||
JSONUI jsonui; | |||
tmp_dsp->buildUserInterface(&jsonui); | |||
std::string json = jsonui.JSON(); | |||
midi_sync = ((json.find("midi") != std::string::npos) && | |||
((json.find("start") != std::string::npos) || | |||
(json.find("stop") != std::string::npos) || | |||
(json.find("clock") != std::string::npos))); | |||
#ifdef NVOICES | |||
nvoices = NVOICES; | |||
#else | |||
MyMeta meta; | |||
tmp_dsp->metadata(&meta); | |||
const char* numVoices = meta.get("nvoices", "0"); | |||
nvoices = atoi(numVoices); | |||
if (nvoices < 0) nvoices = 0; | |||
#endif | |||
delete tmp_dsp; | |||
} | |||
class FaustPolyEngine { | |||
protected: | |||
mydsp_poly* fPolyDSP; // the polyphonic Faust object | |||
dsp* fFinalDSP; // the "final" dsp object submitted to the audio driver | |||
APIUI fAPIUI; // the UI description | |||
string fJSONUI; | |||
string fJSONMeta; | |||
bool fRunning; | |||
audio* fDriver; | |||
midi_handler fMidiHandler; | |||
MidiUI fMidiUI; | |||
public: | |||
FaustPolyEngine(audio* driver = NULL):fMidiUI(&fMidiHandler) | |||
{ | |||
bool midi_sync = false; | |||
int nvoices = 1; | |||
fDriver = driver; | |||
fRunning = false; | |||
mydsp* mono_dsp = new mydsp(); | |||
analyseMeta(midi_sync, nvoices); | |||
// Getting the UI JSON | |||
JSONUI jsonui1(mono_dsp->getNumInputs(), mono_dsp->getNumOutputs()); | |||
mono_dsp->buildUserInterface(&jsonui1); | |||
fJSONUI = jsonui1.JSON(); | |||
// Getting the metadata JSON | |||
JSONUI jsonui1M(mono_dsp->getNumInputs(), mono_dsp->getNumOutputs()); | |||
mono_dsp->metadata(&jsonui1M); | |||
fJSONMeta = jsonui1M.JSON(); | |||
if (fJSONUI.find("keyboard") != std::string::npos | |||
|| fJSONUI.find("poly") != std::string::npos | |||
|| nvoices > 1) { | |||
fPolyDSP = new mydsp_poly(mono_dsp, nvoices, true); | |||
#if POLY2 | |||
fFinalDSP = new dsp_sequencer(fPolyDSP, new effect()); | |||
#else | |||
fFinalDSP = fPolyDSP; | |||
#endif | |||
// Update JSONs with Poly version | |||
JSONUI jsonui2(mono_dsp->getNumInputs(), mono_dsp->getNumOutputs()); | |||
fFinalDSP->buildUserInterface(&jsonui2); | |||
fJSONUI = jsonui2.JSON(); | |||
JSONUI jsonui2M(mono_dsp->getNumInputs(), mono_dsp->getNumOutputs()); | |||
fFinalDSP->metadata(&jsonui2M); | |||
fJSONMeta = jsonui2M.JSON(); | |||
} else { | |||
fPolyDSP = NULL; | |||
fFinalDSP = mono_dsp; | |||
} | |||
fFinalDSP->buildUserInterface(&fMidiUI); | |||
fFinalDSP->buildUserInterface(&fAPIUI); | |||
fDriver->init("Dummy", fFinalDSP); | |||
} | |||
virtual ~FaustPolyEngine() | |||
{ | |||
delete fDriver; | |||
delete fFinalDSP; | |||
} | |||
/* | |||
* start() | |||
* Begins the processing and return true if the connection | |||
* with the audio device was successful and false if not. | |||
*/ | |||
bool start() | |||
{ | |||
if (!fRunning) { | |||
fRunning = fDriver->start(); | |||
} | |||
return fRunning; | |||
} | |||
/* | |||
* isRunning() | |||
* Returns true if the DSP frames are being computed and | |||
* false if not. | |||
*/ | |||
bool isRunning() | |||
{ | |||
return fRunning; | |||
} | |||
/* | |||
* stop() | |||
* Stops the processing, closes the audio engine. | |||
*/ | |||
void stop() | |||
{ | |||
if (fRunning) { | |||
fRunning = false; | |||
fDriver->stop(); | |||
} | |||
} | |||
/* | |||
* keyOn(pitch, velocity) | |||
* Instantiates a new polyphonic voice where velocity | |||
* and pitch are MIDI numbers (0-127). keyOn can only | |||
* be used if the [style:poly] metadata is used in the | |||
* Faust code. keyOn will return 0 if the object is not | |||
* polyphonic and the allocated voice otherwise. | |||
*/ | |||
MapUI* keyOn(int pitch, int velocity) | |||
{ | |||
if (fPolyDSP) { | |||
return fPolyDSP->keyOn(0, pitch, velocity); // MapUI* passed to Java as an integer | |||
} else { | |||
return 0; | |||
} | |||
} | |||
/* | |||
* keyOff(pitch) | |||
* De-instantiates a polyphonic voice where pitch is the | |||
* MIDI number of the note (0-127). keyOff can only be | |||
* used if the [style:poly] metadata is used in the Faust | |||
* code. keyOn will return 0 if the object is not polyphonic | |||
* and 1 otherwise. | |||
*/ | |||
int keyOff(int pitch, int velocity = 127) | |||
{ | |||
if (fPolyDSP) { | |||
fPolyDSP->keyOff(0, pitch, velocity); | |||
return 1; | |||
} else { | |||
return 0; | |||
} | |||
} | |||
/* | |||
* newVoice() | |||
* Instantiate a new voice and returns the corresponding mapUI. | |||
*/ | |||
MapUI* newVoice() | |||
{ | |||
if (fPolyDSP) { | |||
return fPolyDSP->newVoice(); | |||
} else { | |||
return 0; | |||
} | |||
} | |||
/* | |||
* deleteVoice(MapUI* voice) | |||
* Delete a voice based on its MapUI*. | |||
*/ | |||
int deleteVoice(MapUI* voice) | |||
{ | |||
if (fPolyDSP) { | |||
fPolyDSP->deleteVoice(voice); | |||
return 1; | |||
} else { | |||
return 0; | |||
} | |||
} | |||
/* | |||
* deleteVoice(long voice) | |||
* Delete a voice based on its MapUI* casted as a long. | |||
*/ | |||
int deleteVoice(long voice) | |||
{ | |||
return deleteVoice(reinterpret_cast<MapUI*>(voice)); | |||
} | |||
/* | |||
* allNotesOff() | |||
* Gently terminates all the active voices. | |||
*/ | |||
void allNotesOff() | |||
{ | |||
if (fPolyDSP) { | |||
fPolyDSP->allNotesOff(); | |||
} | |||
} | |||
/* | |||
* Propagate MIDI data to the Faust object. | |||
*/ | |||
void propagateMidi(int count, double time, int type, int channel, int data1, int data2) | |||
{ | |||
if (count == 3) fMidiHandler.handleData2(time, type, channel, data1, data2); | |||
else if (count == 2) fMidiHandler.handleData1(time, type, channel, data1); | |||
else if (count == 1) fMidiHandler.handleSync(time, type); | |||
GUI::updateAllGuis(); | |||
} | |||
/* | |||
* getJSONUI() | |||
* Returns a string containing a JSON description of the | |||
* UI of the Faust object. | |||
*/ | |||
const char* getJSONUI() | |||
{ | |||
return fJSONUI.c_str(); | |||
} | |||
/* | |||
* getJSONMeta() | |||
* Returns a string containing a JSON description of the | |||
* metadata of the Faust object. | |||
*/ | |||
const char* getJSONMeta() | |||
{ | |||
return fJSONMeta.c_str(); | |||
} | |||
/* | |||
* buildUserInterface(ui) | |||
* Calls the polyphonic of monophonic buildUserInterface with the ui parameter. | |||
*/ | |||
void buildUserInterface(UI* ui_interface) | |||
{ | |||
fFinalDSP->buildUserInterface(ui_interface); | |||
} | |||
/* | |||
* getParamsCount() | |||
* Returns the number of control parameters of the Faust object. | |||
*/ | |||
int getParamsCount() | |||
{ | |||
return fAPIUI.getParamsCount(); | |||
} | |||
/* | |||
* setParamValue(address, value) | |||
* Sets the value of the parameter associated with address. | |||
*/ | |||
void setParamValue(const char* address, float value) | |||
{ | |||
int id = fAPIUI.getParamIndex(address); | |||
if (id >= 0) { | |||
fAPIUI.setParamValue(id, value); | |||
// In POLY mode, update all voices | |||
GUI::updateAllGuis(); | |||
} | |||
} | |||
/* | |||
* getParamValue(address) | |||
* Takes the address of a parameter and returns its current | |||
* value. | |||
*/ | |||
float getParamValue(const char* address) | |||
{ | |||
int id = fAPIUI.getParamIndex(address); | |||
return (id >= 0) ? fAPIUI.getParamValue(id) : 0.f; | |||
} | |||
/* | |||
* setParamValue(id, value) | |||
* Sets the value of the parameter associated with id. | |||
*/ | |||
void setParamValue(int id, float value) | |||
{ | |||
fAPIUI.setParamValue(id, value); | |||
// In POLY mode, update all voices | |||
GUI::updateAllGuis(); | |||
} | |||
/* | |||
* getParamValue(id) | |||
* Takes the id of a parameter and returns its current | |||
* value. | |||
*/ | |||
float getParamValue(int id) | |||
{ | |||
return fAPIUI.getParamValue(id); | |||
} | |||
/* | |||
* setVoiceParamValue(address, voice, value) | |||
* Sets the value of the parameter associated with address for | |||
* the voice. setVoiceParamValue can only be | |||
* used if the [style:poly] metadata is used in the Faust code. | |||
*/ | |||
void setVoiceParamValue(const char* address, long voice, float value) | |||
{ | |||
reinterpret_cast<MapUI*>(voice)->setParamValue(address, value); | |||
} | |||
/* | |||
* setVoiceParamValue(id, voice, value) | |||
* Sets the value of the parameter associated with the id for | |||
* the voice. setVoiceParamValue can only be | |||
* used if the [style:poly] metadata is used in the Faust code. | |||
*/ | |||
void setVoiceParamValue(int id, long voice, float value) | |||
{ | |||
reinterpret_cast<MapUI*>(voice)->setParamValue(reinterpret_cast<MapUI*>(voice)->getParamAddress(id), value); | |||
} | |||
/* | |||
* getVoiceParamValue(address, voice) | |||
* Gets the parameter value associated with address for the voice. | |||
* getVoiceParamValue can only be used if the [style:poly] metadata | |||
* is used in the Faust code. | |||
*/ | |||
float getVoiceParamValue(const char* address, long voice) | |||
{ | |||
return reinterpret_cast<MapUI*>(voice)->getParamValue(address); | |||
} | |||
/* | |||
* getVoiceParamValue(id, voice) | |||
* Gets the parameter value associated with the id for the voice. | |||
* getVoiceParamValue can only be used if the [style:poly] metadata | |||
* is used in the Faust code. | |||
*/ | |||
float getVoiceParamValue(int id, long voice) | |||
{ | |||
return reinterpret_cast<MapUI*>(voice)->getParamValue(reinterpret_cast<MapUI*>(voice)->getParamAddress(id)); | |||
} | |||
/* | |||
* getParamAddress(id) | |||
* Returns the address of a parameter in function of its "id". | |||
*/ | |||
const char* getParamAddress(int id) | |||
{ | |||
return fAPIUI.getParamAddress(id); | |||
} | |||
/* | |||
* getVoiceParamAddress(id, voice) | |||
* Returns the address of a parameter for a specific voice | |||
* in function of its "id". | |||
*/ | |||
const char* getVoiceParamAddress(int id, long voice) | |||
{ | |||
return reinterpret_cast<MapUI*>(voice)->getParamAddress(id).c_str(); | |||
} | |||
/* | |||
* getParamMin(address) | |||
* Returns the minimum value of a parameter. | |||
*/ | |||
float getParamMin(const char* address) | |||
{ | |||
int id = fAPIUI.getParamIndex(address); | |||
return (id >= 0) ? fAPIUI.getParamMin(id) : 0.f; | |||
} | |||
/* | |||
* getParamMin(id) | |||
* Returns the minimum value of a parameter. | |||
*/ | |||
float getParamMin(int id) | |||
{ | |||
return fAPIUI.getParamMin(id); | |||
} | |||
/* | |||
* getParamMax(address) | |||
* Returns the maximum value of a parameter. | |||
*/ | |||
float getParamMax(const char* address) | |||
{ | |||
int id = fAPIUI.getParamIndex(address); | |||
return (id >= 0) ? fAPIUI.getParamMax(id) : 0.f; | |||
} | |||
/* | |||
* getParamMax(id) | |||
* Returns the maximum value of a parameter. | |||
*/ | |||
float getParamMax(int id) | |||
{ | |||
return fAPIUI.getParamMax(id); | |||
} | |||
/* | |||
* getParamInit(address) | |||
* Returns the default value of a parameter. | |||
*/ | |||
float getParamInit(const char* address) | |||
{ | |||
int id = fAPIUI.getParamIndex(address); | |||
return (id >= 0) ? fAPIUI.getParamInit(id) : 0.f; | |||
} | |||
/* | |||
* getParamInit(id) | |||
* Returns the default value of a parameter. | |||
*/ | |||
float getParamInit(int id) | |||
{ | |||
return fAPIUI.getParamInit(id); | |||
} | |||
/* | |||
* getParamTooltip(address) | |||
* Returns the tooltip of a parameter. | |||
*/ | |||
const char* getParamTooltip(const char* address) | |||
{ | |||
int id = fAPIUI.getParamIndex(address); | |||
return (id >= 0) ? fAPIUI.getParamTooltip(id) : ""; | |||
} | |||
/* | |||
* getParamTooltip(id) | |||
* Returns the tooltip of a parameter. | |||
*/ | |||
const char* getParamTooltip(int id) | |||
{ | |||
return fAPIUI.getParamTooltip(id); | |||
} | |||
/* | |||
* propagateAcc(int acc, float v) | |||
* Propage accelerometer value to the curve conversion layer. | |||
*/ | |||
void propagateAcc(int acc, float v) | |||
{ | |||
fAPIUI.propagateAcc(acc, v); | |||
GUI::updateAllGuis(); | |||
} | |||
/* | |||
* setAccConverter(int p, int acc, int curve, float amin, float amid, float amax) | |||
* Change accelerometer curve mapping. | |||
*/ | |||
void setAccConverter(int p, int acc, int curve, float amin, float amid, float amax) | |||
{ | |||
fAPIUI.setAccConverter(p, acc, curve, amin, amid, amax); | |||
} | |||
/* | |||
* propagateGyr(int gyr, float v) | |||
* Propage gyroscope value to the curve conversion layer. | |||
*/ | |||
void propagateGyr(int gyr, float v) | |||
{ | |||
fAPIUI.propagateGyr(gyr, v); | |||
GUI::updateAllGuis(); | |||
} | |||
/* | |||
* setGyrConverter(int p, int acc, int curve, float amin, float amid, float amax) | |||
* Change gyroscope curve mapping. | |||
*/ | |||
void setGyrConverter(int p, int gyr, int curve, float amin, float amid, float amax) | |||
{ | |||
fAPIUI.setGyrConverter(p, gyr, curve, amin, amid, amax); | |||
} | |||
/* | |||
* getCPULoad() | |||
* Return DSP CPU load. | |||
*/ | |||
float getCPULoad() { return fDriver->get_cpu_load(); } | |||
/* | |||
* getScreenColor() -> c:int | |||
* Get the requested screen color c : | |||
* c < 0 : no screen color requested (keep regular UI) | |||
* c >= 0 : requested color (no UI but a colored screen) | |||
*/ | |||
int getScreenColor() | |||
{ | |||
return fAPIUI.getScreenColor(); | |||
} | |||
}; | |||
// Public C API | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
void destroy(void* dsp) { delete reinterpret_cast<FaustPolyEngine*>(dsp); } | |||
bool start(void* dsp) { return reinterpret_cast<FaustPolyEngine*>(dsp)->start(); } | |||
void stop(void* dsp) { reinterpret_cast<FaustPolyEngine*>(dsp)->stop(); } | |||
bool isRunning(void* dsp) { return reinterpret_cast<FaustPolyEngine*>(dsp)->isRunning(); } | |||
long keyOn(void* dsp, int pitch, int velocity) { return (long)reinterpret_cast<FaustPolyEngine*>(dsp)->keyOn(pitch, velocity); } | |||
int keyOff(void* dsp, int pitch) { return reinterpret_cast<FaustPolyEngine*>(dsp)->keyOff(pitch); } | |||
void propagateMidi(void* dsp, int count, double time, int type, int channel, int data1, int data2) | |||
{ | |||
reinterpret_cast<FaustPolyEngine*>(dsp)->propagateMidi(count, time, type, channel, data1, data2); | |||
} | |||
const char* getJSONUI(void* dsp) { return reinterpret_cast<FaustPolyEngine*>(dsp)->getJSONUI(); } | |||
const char* getJSONMeta(void* dsp) { return reinterpret_cast<FaustPolyEngine*>(dsp)->getJSONMeta(); } | |||
int getParamsCount(void* dsp) { return reinterpret_cast<FaustPolyEngine*>(dsp)->getParamsCount(); } | |||
void setParamValue(void* dsp, const char* address, float value) { reinterpret_cast<FaustPolyEngine*>(dsp)->setParamValue(address, value); } | |||
float getParamValue(void* dsp, const char* address) { return reinterpret_cast<FaustPolyEngine*>(dsp)->getParamValue(address); } | |||
void setParamIdValue(void* dsp, int id, float value) { reinterpret_cast<FaustPolyEngine*>(dsp)->setParamValue(id, value); } | |||
float getParamIdValue(void* dsp, int id) { return reinterpret_cast<FaustPolyEngine*>(dsp)->getParamValue(id); } | |||
void setVoiceParamValue(void* dsp, const char* address, long voice, float value) | |||
{ | |||
reinterpret_cast<FaustPolyEngine*>(dsp)->setVoiceParamValue(address, voice, value); | |||
} | |||
float getVoiceParamValue(void* dsp, const char* address, long voice) { return reinterpret_cast<FaustPolyEngine*>(dsp)->getVoiceParamValue(address, voice); } | |||
const char* getParamAddress(void* dsp, int id) { return reinterpret_cast<FaustPolyEngine*>(dsp)->getParamAddress(id); } | |||
void propagateAcc(void* dsp, int acc, float v) { reinterpret_cast<FaustPolyEngine*>(dsp)->propagateAcc(acc, v); } | |||
void setAccConverter(void* dsp, int p, int acc, int curve, float amin, float amid, float amax) | |||
{ | |||
reinterpret_cast<FaustPolyEngine*>(dsp)->setAccConverter(p, acc, curve, amin, amid, amax); | |||
} | |||
void propagateGyr(void* dsp, int acc, float v) { reinterpret_cast<FaustPolyEngine*>(dsp)->propagateGyr(acc, v); } | |||
void setGyrConverter(void* dsp, int p, int gyr, int curve, float amin, float amid, float amax) | |||
{ | |||
reinterpret_cast<FaustPolyEngine*>(dsp)->setGyrConverter(p, gyr, curve, amin, amid, amax); | |||
} | |||
float getCPULoad(void* dsp) { return reinterpret_cast<FaustPolyEngine*>(dsp)->getCPULoad(); } | |||
int getScreenColor(void* dsp) { return reinterpret_cast<FaustPolyEngine*>(dsp)->getScreenColor(); } | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif // __faust_poly_engine__ |
@@ -0,0 +1,661 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __poly_dsp__ | |||
#define __poly_dsp__ | |||
#include <stdio.h> | |||
#include <string> | |||
#include <math.h> | |||
#include <float.h> | |||
#include <algorithm> | |||
#include <ostream> | |||
#include <sstream> | |||
#include <vector> | |||
#include <limits.h> | |||
#include "faust/gui/MidiUI.h" | |||
#include "faust/gui/JSONUI.h" | |||
#include "faust/gui/MapUI.h" | |||
#include "faust/dsp/proxy-dsp.h" | |||
#define kActiveVoice 0 | |||
#define kFreeVoice -1 | |||
#define kReleaseVoice -2 | |||
#define kNoVoice -3 | |||
#define VOICE_STOP_LEVEL 0.001 | |||
#define MIX_BUFFER_SIZE 16384 | |||
#define FLOAT_MAX(a, b) (((a) < (b)) ? (b) : (a)) | |||
// ends_with(<str>,<end>) : returns true if <str> ends with <end> | |||
static inline bool ends_with(std::string const& str, std::string const& end) | |||
{ | |||
size_t l1 = str.length(); | |||
size_t l2 = end.length(); | |||
return (l1 >= l2) && (0 == str.compare(l1 - l2, l2, end)); | |||
} | |||
static inline double midiToFreq(double note) | |||
{ | |||
return 440.0 * pow(2.0, (note-69.0)/12.0); | |||
} | |||
static inline unsigned int isPowerOfTwo(unsigned int n) | |||
{ | |||
return !(n & (n - 1)); | |||
} | |||
class GroupUI : public GUI, public PathBuilder | |||
{ | |||
private: | |||
std::map<std::string, uiGroupItem*> fLabelZoneMap; | |||
void insertMap(std::string label, FAUSTFLOAT* zone) | |||
{ | |||
if (!ends_with(label, "/gate") | |||
&& !ends_with(label, "/freq") | |||
&& !ends_with(label, "/gain")) { | |||
// Groups all controller except 'freq', 'gate', and 'gain' | |||
if (fLabelZoneMap.find(label) != fLabelZoneMap.end()) { | |||
fLabelZoneMap[label]->addZone(zone); | |||
} else { | |||
fLabelZoneMap[label] = new uiGroupItem(this, zone); | |||
} | |||
} | |||
} | |||
uiCallbackItem* fPanic; | |||
public: | |||
GroupUI(FAUSTFLOAT* zone, uiCallback cb, void* arg) | |||
{ | |||
fPanic = new uiCallbackItem(this, zone, cb, arg); | |||
}; | |||
virtual ~GroupUI() | |||
{ | |||
// 'fPanic' is kept and deleted in GUI, so do not delete here | |||
}; | |||
// -- widget's layouts | |||
void openTabBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
} | |||
void openHorizontalBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
} | |||
void openVerticalBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
} | |||
void closeBox() | |||
{ | |||
fControlsLevel.pop_back(); | |||
} | |||
// -- active widgets | |||
void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
insertMap(buildPath(label), zone); | |||
} | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
insertMap(buildPath(label), zone); | |||
} | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step) | |||
{ | |||
insertMap(buildPath(label), zone); | |||
} | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step) | |||
{ | |||
insertMap(buildPath(label), zone); | |||
} | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step) | |||
{ | |||
insertMap(buildPath(label), zone); | |||
} | |||
// -- passive widgets | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax) | |||
{ | |||
insertMap(buildPath(label), zone); | |||
} | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax) | |||
{ | |||
insertMap(buildPath(label), zone); | |||
} | |||
// -- metadata declarations | |||
void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{} | |||
}; | |||
// One voice of polyphony | |||
struct dsp_voice : public MapUI, public decorator_dsp { | |||
int fNote; // Playing note actual pitch | |||
int fDate; // KeyOn date | |||
bool fTrigger; // True if stolen note and need for envelop re-trigger | |||
FAUSTFLOAT fLevel; // Last audio block level | |||
dsp_voice(dsp* dsp):decorator_dsp(dsp) | |||
{ | |||
dsp->buildUserInterface(this); | |||
fNote = kFreeVoice; | |||
fLevel = FAUSTFLOAT(0); | |||
fDate = 0; | |||
fTrigger = false; | |||
} | |||
void extractLabels(std::string& gate, std::string& freq, std::string& gain) | |||
{ | |||
// Keep gain, freq and gate labels | |||
std::map<std::string, FAUSTFLOAT*>::iterator it; | |||
for (it = getMap().begin(); it != getMap().end(); it++) { | |||
std::string label = (*it).first; | |||
if (ends_with(label, "/gate")) { | |||
gate = label; | |||
} else if (ends_with(label, "/freq")) { | |||
freq = label; | |||
} else if (ends_with(label, "/gain")) { | |||
gain = label; | |||
} | |||
} | |||
} | |||
void computeSlice(int offset, int slice, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
if (slice > 0) { | |||
FAUSTFLOAT** inputs_slice = (FAUSTFLOAT**)alloca(getNumInputs() * sizeof(FAUSTFLOAT*)); | |||
for (int chan = 0; chan < getNumInputs(); chan++) { | |||
inputs_slice[chan] = &(inputs[chan][offset]); | |||
} | |||
FAUSTFLOAT** outputs_slice = (FAUSTFLOAT**)alloca(getNumOutputs() * sizeof(FAUSTFLOAT*)); | |||
for (int chan = 0; chan < getNumOutputs(); chan++) { | |||
outputs_slice[chan] = &(outputs[chan][offset]); | |||
} | |||
compute(slice, inputs_slice, outputs_slice); | |||
} | |||
} | |||
}; | |||
/** | |||
* Polyphonic DSP : group a set of DSP to be played together or triggered by MIDI. | |||
*/ | |||
class mydsp_poly : public dsp, public midi { | |||
private: | |||
dsp* fDSP; | |||
std::vector<dsp_voice*> fVoiceTable; // Individual voices | |||
dsp* fVoiceGroup; // Voices group to be used for GUI grouped control | |||
std::string fGateLabel; | |||
std::string fGainLabel; | |||
std::string fFreqLabel; | |||
FAUSTFLOAT fPanic; | |||
int fPolyphony; | |||
bool fVoiceControl; | |||
bool fGroupControl; | |||
GroupUI fGroups; | |||
FAUSTFLOAT** fMixBuffer; | |||
int fNumOutputs; | |||
int fDate; | |||
std::vector<MidiUI*> fMidiUIList; | |||
inline FAUSTFLOAT mixVoice(int count, FAUSTFLOAT** outputBuffer, FAUSTFLOAT** mixBuffer) | |||
{ | |||
FAUSTFLOAT level = 0; | |||
for (int i = 0; i < fNumOutputs; i++) { | |||
FAUSTFLOAT* mixChannel = mixBuffer[i]; | |||
FAUSTFLOAT* outChannel = outputBuffer[i]; | |||
for (int j = 0; j < count; j++) { | |||
level = FLOAT_MAX(level, (FAUSTFLOAT)fabs(outChannel[j])); | |||
mixChannel[j] += outChannel[j]; | |||
} | |||
} | |||
return level; | |||
} | |||
inline void clearOutput(int count, FAUSTFLOAT** mixBuffer) | |||
{ | |||
for (int i = 0; i < fNumOutputs; i++) { | |||
memset(mixBuffer[i], 0, count * sizeof(FAUSTFLOAT)); | |||
} | |||
} | |||
inline int getVoice(int note, bool steal = false) | |||
{ | |||
for (int i = 0; i < fPolyphony; i++) { | |||
if (fVoiceTable[i]->fNote == note) { | |||
if (steal) { | |||
fVoiceTable[i]->fDate = fDate++; | |||
} | |||
return i; | |||
} | |||
} | |||
if (steal) { | |||
int voice_release = kNoVoice; | |||
int voice_playing = kNoVoice; | |||
int oldest_date_release = INT_MAX; | |||
int oldest_date_playing = INT_MAX; | |||
// Scan all voices | |||
for (int i = 0; i < fPolyphony; i++) { | |||
if (fVoiceTable[i]->fNote == kReleaseVoice) { | |||
// Keeps oldest release voice | |||
if (fVoiceTable[i]->fDate < oldest_date_release) { | |||
oldest_date_release = fVoiceTable[i]->fDate; | |||
voice_release = i; | |||
} | |||
} else { | |||
// Otherwise keeps oldest playing voice | |||
if (fVoiceTable[i]->fDate < oldest_date_playing) { | |||
oldest_date_playing = fVoiceTable[i]->fDate; | |||
voice_playing = i; | |||
} | |||
} | |||
} | |||
// Then decide which one to steal | |||
if (oldest_date_release != INT_MAX) { | |||
std::cout << "Steal release voice : voice_date " << fVoiceTable[voice_release]->fDate << " cur_date = " << fDate << " voice = " << voice_release << std::endl; | |||
fVoiceTable[voice_release]->fDate = fDate++; | |||
fVoiceTable[voice_release]->fTrigger = true; | |||
return voice_release; | |||
} else if (oldest_date_playing != INT_MAX) { | |||
std::cout << "Steal playing voice : voice_date " << fVoiceTable[voice_playing]->fDate << " cur_date = " << fDate << " voice = " << voice_playing << std::endl; | |||
fVoiceTable[voice_playing]->fDate = fDate++; | |||
fVoiceTable[voice_playing]->fTrigger = true; | |||
return voice_playing; | |||
} else { | |||
assert(false); | |||
return kNoVoice; | |||
} | |||
} else { | |||
return kNoVoice; | |||
} | |||
} | |||
inline void init(dsp* dsp, int max_polyphony, bool control, bool group) | |||
{ | |||
fDSP = dsp; | |||
fVoiceControl = control; | |||
fGroupControl = group; | |||
fPolyphony = max_polyphony; | |||
fFreqLabel = fGateLabel = fGainLabel = ""; | |||
// Create voices | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable.push_back(new dsp_voice(dsp->clone())); | |||
} | |||
// Init audio output buffers | |||
fNumOutputs = fVoiceTable[0]->getNumOutputs(); | |||
fMixBuffer = new FAUSTFLOAT*[fNumOutputs]; | |||
for (int i = 0; i < fNumOutputs; i++) { | |||
fMixBuffer[i] = new FAUSTFLOAT[MIX_BUFFER_SIZE]; | |||
} | |||
// Groups all uiItem for a given path | |||
fVoiceGroup = new proxy_dsp(fVoiceTable[0]); | |||
fVoiceGroup->buildUserInterface(&fGroups); | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable[i]->buildUserInterface(&fGroups); | |||
} | |||
fDate = 0; | |||
// Keep gain, freq and gate labels | |||
fVoiceTable[0]->extractLabels(fGateLabel, fFreqLabel, fGainLabel); | |||
} | |||
void uIBuilder(UI* ui_interface) | |||
{ | |||
ui_interface->openTabBox("Polyphonic"); | |||
// Grouped voices UI | |||
ui_interface->openVerticalBox("Voices"); | |||
ui_interface->addButton("Panic", &fPanic); | |||
fVoiceGroup->buildUserInterface(ui_interface); | |||
ui_interface->closeBox(); | |||
// In not group, also add individual voices UI | |||
if (!fGroupControl) { | |||
for (int i = 0; i < fPolyphony; i++) { | |||
char buffer[32]; | |||
snprintf(buffer, 31, ((fPolyphony < 8) ? "Voice%d" : "V%d"), i+1); | |||
ui_interface->openHorizontalBox(buffer); | |||
fVoiceTable[i]->buildUserInterface(ui_interface); | |||
ui_interface->closeBox(); | |||
} | |||
} | |||
ui_interface->closeBox(); | |||
} | |||
static void panic(FAUSTFLOAT val, void* arg) | |||
{ | |||
if (val == FAUSTFLOAT(1)) { | |||
static_cast<mydsp_poly*>(arg)->hardAllNotesOff(); | |||
} | |||
} | |||
inline bool checkPolyphony() | |||
{ | |||
if (fFreqLabel == "") { | |||
std::cout << "DSP is not polyphonic...\n"; | |||
return false; | |||
} else { | |||
return true;; | |||
} | |||
} | |||
// Always returns a voice | |||
int newVoiceAux() | |||
{ | |||
int voice = getVoice(kFreeVoice, true); | |||
assert(voice != kNoVoice); | |||
fVoiceTable[voice]->fNote = kActiveVoice; | |||
return voice; | |||
} | |||
public: | |||
/** | |||
* Constructor. | |||
* | |||
* @param dsp - the dsp to be used for one voice. Beware : mydsp_poly will use and finally delete the pointer. | |||
* @param max_polyphony - number of voices of polyphony | |||
* @param control - whether voices will be dynamically allocated and controlled (typically by a MIDI controler). | |||
* If false all voices are always running. | |||
* @param group - if true, voices are not individually accessible, a global "Voices" tab will automatically dispatch | |||
* a given control on all voices, assuming GUI::updateAllGuis() is called. | |||
* If false, all voices can be individually controlled. | |||
* | |||
*/ | |||
mydsp_poly(dsp* dsp, | |||
int max_polyphony, | |||
bool control = false, | |||
bool group = true): fPanic(FAUSTFLOAT(0)), fGroups(&fPanic, panic, this) | |||
{ | |||
init(dsp, max_polyphony, control, group); | |||
} | |||
void metadata(Meta* meta) { fVoiceTable[0]->metadata(meta); } | |||
virtual ~mydsp_poly() | |||
{ | |||
for (int i = 0; i < fNumOutputs; i++) { | |||
delete[] fMixBuffer[i]; | |||
} | |||
delete[] fMixBuffer; | |||
for (int i = 0; i < fPolyphony; i++) { | |||
delete fVoiceTable[i]; | |||
} | |||
delete fVoiceGroup; | |||
// Remove object from all MidiUI interfaces that handle it | |||
for (int i = 0; i < fMidiUIList.size(); i++) { | |||
fMidiUIList[i]->removeMidiIn(this); | |||
} | |||
delete fDSP; | |||
} | |||
void init(int sample_rate) | |||
{ | |||
// Init voices | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable[i]->init(sample_rate); | |||
} | |||
} | |||
void instanceInit(int sample_rate) | |||
{ | |||
// Init voices | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable[i]->instanceInit(sample_rate); | |||
} | |||
} | |||
void instanceConstants(int sample_rate) | |||
{ | |||
// Init voices | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable[i]->instanceConstants(sample_rate); | |||
} | |||
} | |||
void instanceResetUserInterface() | |||
{ | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable[i]->instanceResetUserInterface(); | |||
} | |||
} | |||
void instanceClear() | |||
{ | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable[i]->instanceClear(); | |||
} | |||
} | |||
virtual int getSampleRate() { return fVoiceTable[0]->getSampleRate(); } | |||
virtual mydsp_poly* clone() | |||
{ | |||
return new mydsp_poly(fDSP->clone(), fPolyphony, fVoiceControl, fGroupControl); | |||
} | |||
void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
assert(count < MIX_BUFFER_SIZE); | |||
// First clear the outputs | |||
clearOutput(count, outputs); | |||
if (fVoiceControl) { | |||
// Mix all playing voices | |||
for (int i = 0; i < fPolyphony; i++) { | |||
if (fVoiceTable[i]->fNote != kFreeVoice) { | |||
if (fVoiceTable[i]->fTrigger) { | |||
// New note, so re-trigger | |||
fVoiceTable[i]->fTrigger = false; | |||
fVoiceTable[i]->setParamValue(fGateLabel, 0.0f); | |||
fVoiceTable[i]->computeSlice(0, 1, inputs, fMixBuffer); | |||
fVoiceTable[i]->setParamValue(fGateLabel, 1.0f); | |||
fVoiceTable[i]->computeSlice(1, count - 1, inputs, fMixBuffer); | |||
} else { | |||
// Compute regular voice | |||
fVoiceTable[i]->compute(count, inputs, fMixBuffer); | |||
} | |||
// Mix it in result | |||
fVoiceTable[i]->fLevel = mixVoice(count, fMixBuffer, outputs); | |||
// Check the level to possibly set the voice in kFreeVoice again | |||
if ((fVoiceTable[i]->fLevel < VOICE_STOP_LEVEL) && (fVoiceTable[i]->fNote == kReleaseVoice)) { | |||
fVoiceTable[i]->fNote = kFreeVoice; | |||
} | |||
} | |||
} | |||
} else { | |||
// Mix all voices | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable[i]->compute(count, inputs, fMixBuffer); | |||
mixVoice(count, fMixBuffer, outputs); | |||
} | |||
} | |||
} | |||
int getNumInputs() | |||
{ | |||
return fVoiceTable[0]->getNumInputs(); | |||
} | |||
int getNumOutputs() | |||
{ | |||
return fVoiceTable[0]->getNumOutputs(); | |||
} | |||
void buildUserInterface(UI* ui_interface) | |||
{ | |||
// Add itself to the MidiUI object | |||
MidiUI* midi_ui = dynamic_cast<MidiUI*>(ui_interface); | |||
if (midi_ui) { | |||
fMidiUIList.push_back(midi_ui); | |||
midi_ui->addMidiIn(this); | |||
} | |||
if (fPolyphony > 1) { | |||
uIBuilder(ui_interface); | |||
} else { | |||
fVoiceTable[0]->buildUserInterface(ui_interface); | |||
} | |||
} | |||
MapUI* newVoice() | |||
{ | |||
return fVoiceTable[newVoiceAux()]; | |||
} | |||
void deleteVoice(MapUI* voice) | |||
{ | |||
std::vector<dsp_voice*>::iterator it = find(fVoiceTable.begin(), fVoiceTable.end(), reinterpret_cast<dsp_voice*>(voice)); | |||
if (it != fVoiceTable.end()) { | |||
(*it)->setParamValue(fGateLabel, 0.0f); | |||
// Release voice | |||
(*it)->fNote = kReleaseVoice; | |||
} else { | |||
std::cout << "Voice not found\n"; | |||
} | |||
} | |||
// Pure MIDI control | |||
MapUI* keyOn(int channel, int pitch, int velocity) | |||
{ | |||
if (checkPolyphony()) { | |||
int voice = newVoiceAux(); | |||
fVoiceTable[voice]->setParamValue(fFreqLabel, midiToFreq(pitch)); | |||
fVoiceTable[voice]->setParamValue(fGainLabel, float(velocity)/127.f); | |||
fVoiceTable[voice]->fNote = pitch; | |||
fVoiceTable[voice]->fTrigger = true; // so that envelop is always re-initialized | |||
return fVoiceTable[voice]; | |||
} else { | |||
return 0; | |||
} | |||
} | |||
void keyOff(int channel, int pitch, int velocity = 127) | |||
{ | |||
if (checkPolyphony()) { | |||
int voice = getVoice(pitch); | |||
if (voice != kNoVoice) { | |||
// No use of velocity for now... | |||
fVoiceTable[voice]->setParamValue(fGateLabel, 0.0f); | |||
// Release voice | |||
fVoiceTable[voice]->fNote = kReleaseVoice; | |||
} else { | |||
std::cout << "Playing pitch = " << pitch << " not found\n"; | |||
} | |||
} | |||
} | |||
void pitchWheel(int channel, int wheel) | |||
{} | |||
void ctrlChange(int channel, int ctrl, int value) | |||
{ | |||
if (ctrl == ALL_NOTES_OFF || ctrl == ALL_SOUND_OFF) { | |||
allNotesOff(); | |||
} | |||
} | |||
void progChange(int channel, int pgm) | |||
{} | |||
void keyPress(int channel, int pitch, int press) | |||
{} | |||
void chanPress(int channel, int press) | |||
{} | |||
void ctrlChange14bits(int channel, int ctrl, int value) | |||
{} | |||
// Gently terminates all the active voice | |||
void allNotesOff() | |||
{ | |||
if (checkPolyphony()) { | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable[i]->setParamValue(fGateLabel, 0.0f); | |||
fVoiceTable[i]->fNote = kReleaseVoice; | |||
} | |||
} | |||
} | |||
// Kill immediately all the active voices | |||
void hardAllNotesOff() | |||
{ | |||
if (checkPolyphony()) { | |||
for (int i = 0; i < fPolyphony; i++) { | |||
fVoiceTable[i]->setParamValue(fGateLabel, 0.0f); | |||
// Stops immediately | |||
fVoiceTable[i]->fNote = kFreeVoice; | |||
fVoiceTable[i]->fTrigger = false; | |||
} | |||
} | |||
} | |||
}; | |||
#endif // __poly_dsp__ |
@@ -0,0 +1,277 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __proxy_dsp__ | |||
#define __proxy_dsp__ | |||
#include <vector> | |||
#include <map> | |||
#include "faust/dsp/dsp.h" | |||
#include "faust/gui/SimpleParser.h" | |||
#include "faust/gui/JSONUI.h" | |||
#ifdef _WIN32 | |||
#include <windows.h> | |||
#define snprintf _snprintf | |||
#endif | |||
inline FAUSTFLOAT STR2REAL(const std::string& s) { return (strtod(s.c_str(), NULL)); } | |||
//------------------------------------------------------------------- | |||
// Decode a dsp JSON description and implement 'buildUserInterface' | |||
//------------------------------------------------------------------- | |||
struct JSONUIDecoder { | |||
std::string fName; | |||
std::map<std::string, std::string> fMetadatas; | |||
std::vector<itemInfo*> fUiItems; | |||
FAUSTFLOAT* fInControl; | |||
FAUSTFLOAT* fOutControl; | |||
std::string fJSON; | |||
int fNumInputs, fNumOutputs; | |||
int fInputItems, fOutputItems; | |||
JSONUIDecoder(const std::string& json) | |||
{ | |||
fJSON = json; | |||
const char* p = fJSON.c_str(); | |||
parseJson(p, fMetadatas, fUiItems); | |||
// fMetadatas will contain the "meta" section as well as <name : val>, <inputs : val>, <ouputs : val> pairs | |||
if (fMetadatas.find("name") != fMetadatas.end()) { | |||
fName = fMetadatas["name"]; | |||
fMetadatas.erase("name"); | |||
} else { | |||
fName = ""; | |||
} | |||
if (fMetadatas.find("inputs") != fMetadatas.end()) { | |||
fNumInputs = atoi(fMetadatas["inputs"].c_str()); | |||
fMetadatas.erase("inputs"); | |||
} else { | |||
fNumInputs = -1; | |||
} | |||
if (fMetadatas.find("outputs") != fMetadatas.end()) { | |||
fNumOutputs = atoi(fMetadatas["outputs"].c_str()); | |||
fMetadatas.erase("outputs"); | |||
} else { | |||
fNumOutputs = -1; | |||
} | |||
vector<itemInfo*>::iterator it; | |||
fInputItems = 0; | |||
fOutputItems = 0; | |||
for (it = fUiItems.begin(); it != fUiItems.end(); it++) { | |||
string type = (*it)->type; | |||
if (type == "vslider" || type == "hslider" || type == "nentry" || type == "button") { | |||
fInputItems++; | |||
} else if (type == "hbargraph" || type == "vbargraph") { | |||
fOutputItems++; | |||
} | |||
} | |||
fInControl = new FAUSTFLOAT[fInputItems]; | |||
fOutControl = new FAUSTFLOAT[fOutputItems]; | |||
} | |||
virtual ~JSONUIDecoder() | |||
{ | |||
vector<itemInfo*>::iterator it; | |||
for (it = fUiItems.begin(); it != fUiItems.end(); it++) { | |||
delete(*it); | |||
} | |||
delete [] fInControl; | |||
delete [] fOutControl; | |||
} | |||
void metadata(Meta* m) | |||
{ | |||
std::map<std::string, std::string>::iterator it; | |||
for (it = fMetadatas.begin(); it != fMetadatas.end(); it++) { | |||
m->declare((*it).first.c_str(), (*it).second.c_str()); | |||
} | |||
} | |||
void buildUserInterface(UI* ui) | |||
{ | |||
// To be sure the floats are correctly encoded | |||
char* tmp_local = setlocale(LC_ALL, NULL); | |||
setlocale(LC_ALL, "C"); | |||
int counterIn = 0; | |||
int counterOut = 0; | |||
vector<itemInfo*>::iterator it; | |||
for (it = fUiItems.begin(); it != fUiItems.end(); it++) { | |||
bool isInItem = false; | |||
bool isOutItem = false; | |||
string type = (*it)->type; | |||
FAUSTFLOAT init = STR2REAL((*it)->init); | |||
FAUSTFLOAT min = STR2REAL((*it)->min); | |||
FAUSTFLOAT max = STR2REAL((*it)->max); | |||
FAUSTFLOAT step = STR2REAL((*it)->step); | |||
if (type == "vslider" || type == "hslider" || type == "nentry" || type == "button") { | |||
isInItem = true; | |||
} else if (type == "hbargraph" || type == "vbargraph") { | |||
isOutItem = true; | |||
} | |||
// Meta data declaration for input items | |||
if ((*it)->type.find("group") == string::npos && (*it)->type.find("bargraph") == string::npos && (*it)->type != "close") { | |||
fInControl[counterIn] = init; | |||
for (int i = 0; i < (*it)->meta.size(); i++) { | |||
ui->declare(&fInControl[counterIn], (*it)->meta[i].first.c_str(), (*it)->meta[i].second.c_str()); | |||
} | |||
} | |||
// Meta data declaration for output items | |||
else if ((*it)->type.find("bargraph") != string::npos) { | |||
fOutControl[counterOut] = init; | |||
for (int i = 0; i < (*it)->meta.size(); i++) { | |||
ui->declare(&fOutControl[counterOut], (*it)->meta[i].first.c_str(), (*it)->meta[i].second.c_str()); | |||
} | |||
} | |||
// Meta data declaration for group opening or closing | |||
else { | |||
for (int i = 0; i < (*it)->meta.size(); i++) { | |||
ui->declare(0, (*it)->meta[i].first.c_str(), (*it)->meta[i].second.c_str()); | |||
} | |||
} | |||
if (type == "hgroup") { | |||
ui->openHorizontalBox((*it)->label.c_str()); | |||
} else if (type == "vgroup") { | |||
ui->openVerticalBox((*it)->label.c_str()); | |||
} else if (type == "tgroup") { | |||
ui->openTabBox((*it)->label.c_str()); | |||
} else if (type == "vslider") { | |||
ui->addVerticalSlider((*it)->label.c_str(), &fInControl[counterIn], init, min, max, step); | |||
} else if (type == "hslider") { | |||
ui->addHorizontalSlider((*it)->label.c_str(), &fInControl[counterIn], init, min, max, step); | |||
} else if (type == "checkbox") { | |||
ui->addCheckButton((*it)->label.c_str(), &fInControl[counterIn]); | |||
} else if (type == "hbargraph") { | |||
ui->addHorizontalBargraph((*it)->label.c_str(), &fOutControl[counterOut], min, max); | |||
} else if (type == "vbargraph") { | |||
ui->addVerticalBargraph((*it)->label.c_str(), &fOutControl[counterOut], min, max); | |||
} else if (type == "nentry") { | |||
ui->addNumEntry((*it)->label.c_str(), &fInControl[counterIn], init, min, max, step); | |||
} else if (type == "button") { | |||
ui->addButton((*it)->label.c_str(), &fInControl[counterIn]); | |||
} else if (type == "close") { | |||
ui->closeBox(); | |||
} | |||
if (isInItem) { | |||
counterIn++; | |||
} | |||
if (isOutItem) { | |||
counterOut++; | |||
} | |||
} | |||
setlocale(LC_ALL, tmp_local); | |||
} | |||
}; | |||
//---------------------------------------------------------------- | |||
// Proxy dsp definition created from the DSP JSON description | |||
// This class allows a 'proxy' dsp to control a real dsp | |||
// possibly running somewhere else. | |||
//---------------------------------------------------------------- | |||
class proxy_dsp : public dsp { | |||
private: | |||
int fSamplingFreq; | |||
JSONUIDecoder* fDecoder; | |||
public: | |||
proxy_dsp(const string& json) | |||
{ | |||
fDecoder = new JSONUIDecoder(json); | |||
fSamplingFreq = -1; | |||
} | |||
proxy_dsp(dsp* dsp) | |||
{ | |||
JSONUI builder(dsp->getNumInputs(), dsp->getNumOutputs()); | |||
dsp->metadata(&builder); | |||
dsp->buildUserInterface(&builder); | |||
fSamplingFreq = dsp->getSampleRate(); | |||
fDecoder = new JSONUIDecoder(builder.JSON()); | |||
} | |||
virtual ~proxy_dsp() | |||
{ | |||
delete fDecoder; | |||
} | |||
virtual int getNumInputs() { return fDecoder->fNumInputs; } | |||
virtual int getNumOutputs() { return fDecoder->fNumOutputs; } | |||
virtual void buildUserInterface(UI* ui) { fDecoder->buildUserInterface(ui); } | |||
// To possibly implement in a concrete proxy dsp | |||
virtual void init(int samplingRate) { fSamplingFreq = samplingRate; } | |||
virtual void instanceInit(int samplingRate) {} | |||
virtual void instanceConstants(int samplingRate) {} | |||
virtual void instanceResetUserInterface() {} | |||
virtual void instanceClear() {} | |||
virtual int getSampleRate() { return fSamplingFreq; } | |||
virtual proxy_dsp* clone() { return new proxy_dsp(fDecoder->fJSON); } | |||
virtual void metadata(Meta* m) { fDecoder->metadata(m); } | |||
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {} | |||
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {} | |||
}; | |||
#endif |
@@ -0,0 +1,287 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __timed_dsp__ | |||
#define __timed_dsp__ | |||
#include "faust/dsp/dsp.h" | |||
#include "faust/gui/GUI.h" | |||
#include "faust/gui/ring-buffer.h" | |||
#include <set> | |||
#include <float.h> | |||
#include <assert.h> | |||
namespace { | |||
#if __APPLE__ | |||
#if TARGET_OS_IPHONE | |||
//inline double GetCurrentTimeInUsec() { return double(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000.; } | |||
// TODO | |||
inline double GetCurrentTimeInUsec() { return 0.0; } | |||
#else | |||
#include <CoreAudio/HostTime.h> | |||
inline double GetCurrentTimeInUsec() { return double(AudioConvertHostTimeToNanos(AudioGetCurrentHostTime())) / 1000.; } | |||
#endif | |||
#endif | |||
#if __linux__ | |||
#include <sys/time.h> | |||
inline double GetCurrentTimeInUsec() | |||
{ | |||
struct timeval tv; | |||
(void)gettimeofday(&tv, (struct timezone *)NULL); | |||
return double((tv.tv_sec * 1000000) + tv.tv_usec); | |||
} | |||
#endif | |||
#if _WIN32 | |||
#include <Windows.h> | |||
inline double GetCurrentTimeInUsec(void) | |||
{ | |||
LARGE_INTEGER time; | |||
LARGE_INTEGER frequency; | |||
QueryPerformanceFrequency(&frequency); | |||
QueryPerformanceCounter(&time); | |||
return double(time.QuadPart) / double(frequency.QuadPart) * 1000000.0; | |||
} | |||
#endif | |||
} | |||
/** | |||
* ZoneUI : this class collect zones in a set. | |||
*/ | |||
struct ZoneUI : public UI | |||
{ | |||
std::set<FAUSTFLOAT*> fZoneSet; | |||
ZoneUI() {}; | |||
virtual ~ZoneUI() {}; | |||
void insertZone(FAUSTFLOAT* zone) | |||
{ | |||
if (GUI::gTimedZoneMap.find(zone) != GUI::gTimedZoneMap.end()) { | |||
fZoneSet.insert(zone); | |||
} | |||
} | |||
// -- widget's layouts | |||
void openTabBox(const char* label) | |||
{} | |||
void openHorizontalBox(const char* label) | |||
{} | |||
void openVerticalBox(const char* label) | |||
{} | |||
void closeBox() | |||
{} | |||
// -- active widgets | |||
void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
insertZone(zone); | |||
} | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
insertZone(zone); | |||
} | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step) | |||
{ | |||
insertZone(zone); | |||
} | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step) | |||
{ | |||
insertZone(zone); | |||
} | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step) | |||
{ | |||
insertZone(zone); | |||
} | |||
// -- passive widgets | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax) | |||
{ | |||
insertZone(zone); | |||
} | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax) | |||
{ | |||
insertZone(zone); | |||
} | |||
// -- metadata declarations | |||
void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{} | |||
}; | |||
/** | |||
* Timed signal processor that allows to handle the decorated DSP by 'slices' | |||
* that is, calling the 'compute' method several times and changing control | |||
* parameters between slices. | |||
*/ | |||
class timed_dsp : public decorator_dsp { | |||
protected: | |||
double fDateUsec; // Compute call date in usec | |||
double fOffsetUsec; // Compute call offset in usec | |||
bool fFirstCallback; | |||
ZoneUI fZoneUI; | |||
void computeSlice(int offset, int slice, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
if (slice > 0) { | |||
FAUSTFLOAT** inputs_slice = (FAUSTFLOAT**)alloca(fDSP->getNumInputs() * sizeof(FAUSTFLOAT*)); | |||
for (int chan = 0; chan < fDSP->getNumInputs(); chan++) { | |||
inputs_slice[chan] = &(inputs[chan][offset]); | |||
} | |||
FAUSTFLOAT** outputs_slice = (FAUSTFLOAT**)alloca(fDSP->getNumOutputs() * sizeof(FAUSTFLOAT*)); | |||
for (int chan = 0; chan < fDSP->getNumOutputs(); chan++) { | |||
outputs_slice[chan] = &(outputs[chan][offset]); | |||
} | |||
fDSP->compute(slice, inputs_slice, outputs_slice); | |||
} | |||
} | |||
double convertUsecToSample(double usec) | |||
{ | |||
return std::max(0., (double(getSampleRate()) * (usec - fDateUsec)) / 1000000.); | |||
} | |||
ztimedmap::iterator getNextControl(DatedControl& res, bool convert_ts) | |||
{ | |||
DatedControl date1(DBL_MAX, 0); | |||
ztimedmap::iterator it1, it2 = GUI::gTimedZoneMap.end(); | |||
std::set<FAUSTFLOAT*>::iterator it3; | |||
// Find date of next audio slice to compute | |||
for (it3 = fZoneUI.fZoneSet.begin(); it3 != fZoneUI.fZoneSet.end(); it3++) { | |||
// If value list is not empty, get the date and keep the minimal one | |||
it1 = GUI::gTimedZoneMap.find(*it3); | |||
if (it1 != GUI::gTimedZoneMap.end()) { // Check if zone still in global GUI::gTimedZoneMap (since MidiUI may have been desallocated) | |||
DatedControl date2; | |||
if (ringbuffer_peek((*it1).second, (char*)&date2, sizeof(DatedControl)) == sizeof(DatedControl) | |||
&& date2.fDate < date1.fDate) { | |||
it2 = it1; | |||
date1 = date2; | |||
} | |||
} | |||
} | |||
// If needed, convert date1 in samples from begining of the buffer, possible moving to 0 (if negative) | |||
if (convert_ts) { | |||
date1.fDate = convertUsecToSample(date1.fDate); | |||
} | |||
res = date1; | |||
return it2; | |||
} | |||
virtual void computeAux(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs, bool convert_ts) | |||
{ | |||
int slice, offset = 0; | |||
ztimedmap::iterator it; | |||
DatedControl next_control; | |||
// Do audio computation "slice" by "slice" | |||
while ((it = getNextControl(next_control, convert_ts)) != GUI::gTimedZoneMap.end()) { | |||
// Compute audio slice | |||
slice = int(next_control.fDate) - offset; | |||
computeSlice(offset, slice, inputs, outputs); | |||
offset += slice; | |||
// Update control | |||
ringbuffer_t* control_values = (*it).second; | |||
*((*it).first) = next_control.fValue; | |||
// Move ringbuffer pointer | |||
ringbuffer_read_advance(control_values, sizeof(DatedControl)); | |||
} | |||
// Compute last audio slice | |||
slice = count - offset; | |||
computeSlice(offset, slice, inputs, outputs); | |||
} | |||
public: | |||
timed_dsp(dsp* dsp):decorator_dsp(dsp), fDateUsec(0),fOffsetUsec(0), fFirstCallback(true) | |||
{} | |||
virtual ~timed_dsp() | |||
{} | |||
virtual void init(int samplingRate) | |||
{ | |||
fDSP->init(samplingRate); | |||
} | |||
virtual void buildUserInterface(UI* ui_interface) | |||
{ | |||
fDSP->buildUserInterface(ui_interface); | |||
// Only keep zones that are in GUI::gTimedZoneMap | |||
fDSP->buildUserInterface(&fZoneUI); | |||
} | |||
virtual timed_dsp* clone() | |||
{ | |||
return new timed_dsp(fDSP->clone()); | |||
} | |||
// Default method take a timestamp at 'compute' call time | |||
virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
compute(::GetCurrentTimeInUsec(), count, inputs, outputs); | |||
} | |||
virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) | |||
{ | |||
if (date_usec == -1) { | |||
// JACK mode : timestamp is already in frames | |||
computeAux(count, inputs, outputs, false); | |||
} else { | |||
// Save the timestamp offset in the first callback | |||
if (fFirstCallback) { | |||
fOffsetUsec = ::GetCurrentTimeInUsec() - date_usec; | |||
fDateUsec = date_usec + fOffsetUsec; | |||
fFirstCallback = false; | |||
} | |||
// RtMidi mode : timestamp must be converted in frames | |||
computeAux(count, inputs, outputs, true); | |||
// Keep call date | |||
fDateUsec = date_usec + fOffsetUsec; | |||
} | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,472 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef API_UI_H | |||
#define API_UI_H | |||
#include "faust/gui/meta.h" | |||
#include "faust/gui/UI.h" | |||
#include "faust/gui/PathBuilder.h" | |||
#include "faust/gui/ValueConverter.h" | |||
#include <sstream> | |||
#include <string> | |||
#include <vector> | |||
#include <iostream> | |||
#include <map> | |||
enum { kLin = 0, kLog = 1, kExp = 2 }; | |||
class APIUI : public PathBuilder, public Meta, public UI | |||
{ | |||
protected: | |||
int fNumParameters; | |||
std::vector<std::string> fName; | |||
std::map<std::string, int> fPathMap; | |||
std::map<std::string, int> fLabelMap; | |||
std::vector<ValueConverter*> fConversion; | |||
std::vector<FAUSTFLOAT*> fZone; | |||
std::vector<FAUSTFLOAT> fInit; | |||
std::vector<FAUSTFLOAT> fMin; | |||
std::vector<FAUSTFLOAT> fMax; | |||
std::vector<FAUSTFLOAT> fStep; | |||
std::vector<std::string> fUnit; | |||
std::vector<std::string> fTooltip; | |||
std::vector<ZoneControl*> fAcc[3]; | |||
std::vector<ZoneControl*> fGyr[3]; | |||
// Screen color control | |||
// "...[screencolor:red]..." etc. | |||
bool fHasScreenControl; // true if control screen color metadata | |||
ZoneReader* fRedReader; | |||
ZoneReader* fGreenReader; | |||
ZoneReader* fBlueReader; | |||
// Current values controlled by metadata | |||
std::string fCurrentUnit; | |||
int fCurrentScale; | |||
std::string fCurrentAcc; | |||
std::string fCurrentGyr; | |||
std::string fCurrentColor; | |||
std::string fCurrentTooltip; | |||
// Add a generic parameter | |||
virtual void addParameter(const char* label, | |||
FAUSTFLOAT* zone, | |||
FAUSTFLOAT init, | |||
FAUSTFLOAT min, | |||
FAUSTFLOAT max, | |||
FAUSTFLOAT step) | |||
{ | |||
std::string path = buildPath(label); | |||
fPathMap[path] = fLabelMap[label] = fNumParameters++; | |||
fName.push_back(path); | |||
fZone.push_back(zone); | |||
fInit.push_back(init); | |||
fMin.push_back(min); | |||
fMax.push_back(max); | |||
fStep.push_back(step); | |||
//handle unit metadata | |||
fUnit.push_back(fCurrentUnit); | |||
fCurrentUnit = ""; | |||
//handle tooltip metadata | |||
fTooltip.push_back(fCurrentTooltip); | |||
fCurrentTooltip = ""; | |||
//handle scale metadata | |||
switch (fCurrentScale) { | |||
case kLin : fConversion.push_back(new LinearValueConverter(0,1, min, max)); break; | |||
case kLog : fConversion.push_back(new LogValueConverter(0,1, min, max)); break; | |||
case kExp : fConversion.push_back(new ExpValueConverter(0,1, min, max)); break; | |||
} | |||
fCurrentScale = kLin; | |||
// handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..." | |||
if (fCurrentAcc.size() > 0) { | |||
std::istringstream iss(fCurrentAcc); | |||
int axe, curve; | |||
double amin, amid, amax; | |||
iss >> axe >> curve >> amin >> amid >> amax; | |||
if ((0 <= axe) && (axe < 3) && | |||
(0 <= curve) && (curve < 4) && | |||
(amin < amax) && (amin <= amid) && (amid <= amax)) | |||
{ | |||
fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); | |||
} else { | |||
std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl; | |||
} | |||
} | |||
fCurrentAcc = ""; | |||
// handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..." | |||
if (fCurrentGyr.size() > 0) { | |||
std::istringstream iss(fCurrentGyr); | |||
int axe, curve; | |||
double amin, amid, amax; | |||
iss >> axe >> curve >> amin >> amid >> amax; | |||
if ((0 <= axe) && (axe < 3) && | |||
(0 <= curve) && (curve < 4) && | |||
(amin < amax) && (amin <= amid) && (amid <= amax)) | |||
{ | |||
fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); | |||
} else { | |||
std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl; | |||
} | |||
} | |||
fCurrentGyr = ""; | |||
// handle screencolor metadata "...[screencolor:red|green|blue]..." | |||
if (fCurrentColor.size() > 0) { | |||
if ((fCurrentColor == "red") && (fRedReader == 0)) { | |||
fRedReader = new ZoneReader(zone, min, max); | |||
fHasScreenControl = true; | |||
} else if ((fCurrentColor == "green") && (fGreenReader == 0)) { | |||
fGreenReader = new ZoneReader(zone, min, max); | |||
fHasScreenControl = true; | |||
} else if ((fCurrentColor == "blue") && (fBlueReader == 0)) { | |||
fBlueReader = new ZoneReader(zone, min, max); | |||
fHasScreenControl = true; | |||
} else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) { | |||
fRedReader = new ZoneReader(zone, min, max); | |||
fGreenReader = new ZoneReader(zone, min, max); | |||
fBlueReader = new ZoneReader(zone, min, max); | |||
fHasScreenControl = true; | |||
} else { | |||
std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl; | |||
} | |||
} | |||
fCurrentColor = ""; | |||
} | |||
int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val) | |||
{ | |||
FAUSTFLOAT* zone = fZone[p]; | |||
for (int i = 0; i < table[val].size(); i++) { | |||
if (zone == table[val][i]->getZone()) return i; | |||
} | |||
return -1; | |||
} | |||
void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax) | |||
{ | |||
int id1 = getZoneIndex(table, p, 0); | |||
int id2 = getZoneIndex(table, p, 1); | |||
int id3 = getZoneIndex(table, p, 2); | |||
// Deactivates everywhere.. | |||
if (id1 != -1) table[0][id1]->setActive(false); | |||
if (id2 != -1) table[1][id2]->setActive(false); | |||
if (id3 != -1) table[2][id3]->setActive(false); | |||
if (val == -1) { // Means: no more mapping... | |||
// So stay all deactivated... | |||
} else { | |||
int id4 = getZoneIndex(table, p, val); | |||
if (id4 != -1) { | |||
// Reactivate the one we edit... | |||
table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]); | |||
table[val][id4]->setActive(true); | |||
} else { | |||
// Allocate a new CurveZoneControl which is 'active' by default | |||
FAUSTFLOAT* zone = fZone[p]; | |||
table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p])); | |||
} | |||
} | |||
} | |||
void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax) | |||
{ | |||
int id1 = getZoneIndex(table, p, 0); | |||
int id2 = getZoneIndex(table, p, 1); | |||
int id3 = getZoneIndex(table, p, 2); | |||
if (id1 != -1) { | |||
val = 0; | |||
curve = fAcc[val][id1]->getCurve(); | |||
table[val][id1]->getMappingValues(amin, amid, amax); | |||
} else if (id2 != -1) { | |||
val = 1; | |||
curve = fAcc[val][id2]->getCurve(); | |||
table[val][id2]->getMappingValues(amin, amid, amax); | |||
} else if (id3 != -1) { | |||
val = 2; | |||
curve = fAcc[val][id3]->getCurve(); | |||
table[val][id3]->getMappingValues(amin, amid, amax); | |||
} else { | |||
val = -1; // No mapping | |||
curve = 0; | |||
amin = -100.; | |||
amid = 0.; | |||
amax = 100.; | |||
} | |||
} | |||
public: | |||
APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0) | |||
{} | |||
virtual ~APIUI() | |||
{ | |||
std::vector<ValueConverter*>::iterator it1; | |||
for (it1 = fConversion.begin(); it1 != fConversion.end(); it1++) { | |||
delete(*it1); | |||
} | |||
std::vector<ZoneControl*>::iterator it2; | |||
for (int i = 0; i < 3; i++) { | |||
for (it2 = fAcc[i].begin(); it2 != fAcc[i].end(); it2++) { | |||
delete(*it2); | |||
} | |||
for (it2 = fGyr[i].begin(); it2 != fGyr[i].end(); it2++) { | |||
delete(*it2); | |||
} | |||
} | |||
delete fRedReader; | |||
delete fGreenReader; | |||
delete fBlueReader; | |||
} | |||
// -- widget's layouts | |||
virtual void openTabBox(const char* label) { fControlsLevel.push_back(label); } | |||
virtual void openHorizontalBox(const char* label) { fControlsLevel.push_back(label); } | |||
virtual void openVerticalBox(const char* label) { fControlsLevel.push_back(label); } | |||
virtual void closeBox() { fControlsLevel.pop_back(); } | |||
// -- active widgets | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
addParameter(label, zone, 0, 0, 1, 1); | |||
} | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
addParameter(label, zone, 0, 0, 1, 1); | |||
} | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addParameter(label, zone, init, min, max, step); | |||
} | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addParameter(label, zone, init, min, max, step); | |||
} | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addParameter(label, zone, init, min, max, step); | |||
} | |||
// -- passive widgets | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
addParameter(label, zone, min, min, max, (max-min)/1000.0); | |||
} | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
addParameter(label, zone, min, min, max, (max-min)/1000.0); | |||
} | |||
// -- metadata declarations | |||
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{ | |||
if (strcmp(key, "scale") == 0) { | |||
if (strcmp(val, "log") == 0) { | |||
fCurrentScale = kLog; | |||
} else if (strcmp(val, "exp") == 0) { | |||
fCurrentScale = kExp; | |||
} else { | |||
fCurrentScale = kLin; | |||
} | |||
} else if (strcmp(key, "unit") == 0) { | |||
fCurrentUnit = val; | |||
} else if (strcmp(key, "acc") == 0) { | |||
fCurrentAcc = val; | |||
} else if (strcmp(key, "gyr") == 0) { | |||
fCurrentGyr = val; | |||
} else if (strcmp(key, "screencolor") == 0) { | |||
fCurrentColor = val; // val = "red", "green" or "blue" | |||
} else if (strcmp(key, "tooltip") == 0) { | |||
fCurrentTooltip = val; | |||
} | |||
} | |||
virtual void declare(const char* key, const char* val) | |||
{} | |||
//------------------------------------------------------------------------------- | |||
// Simple API part | |||
//------------------------------------------------------------------------------- | |||
int getParamsCount() { return fNumParameters; } | |||
int getParamIndex(const char* path) | |||
{ | |||
if (fPathMap.find(path) != fPathMap.end()) { | |||
return fPathMap[path]; | |||
} else if (fLabelMap.find(path) != fLabelMap.end()) { | |||
return fLabelMap[path]; | |||
} else { | |||
return -1; | |||
} | |||
} | |||
const char* getParamAddress(int p) { return fName[p].c_str(); } | |||
const char* getParamUnit(int p) { return fUnit[p].c_str(); } | |||
const char* getParamTooltip(int p) { return fTooltip[p].c_str(); } | |||
FAUSTFLOAT getParamMin(int p) { return fMin[p]; } | |||
FAUSTFLOAT getParamMax(int p) { return fMax[p]; } | |||
FAUSTFLOAT getParamStep(int p) { return fStep[p]; } | |||
FAUSTFLOAT getParamInit(int p) { return fInit[p]; } | |||
FAUSTFLOAT* getParamZone(int p) { return fZone[p]; } | |||
FAUSTFLOAT getParamValue(int p) { return *fZone[p]; } | |||
void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; } | |||
double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); } | |||
void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); } | |||
double value2ratio(int p, double r) { return fConversion[p]->faust2ui(r); } | |||
double ratio2value(int p, double r) { return fConversion[p]->ui2faust(r); } | |||
/** | |||
* Set a new value coming from an accelerometer, propagate it to all relevant float* zones. | |||
* | |||
* @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer | |||
* @param value - the new value | |||
* | |||
*/ | |||
void propagateAcc(int acc, double value) | |||
{ | |||
for (int i = 0; i < fAcc[acc].size(); i++) { | |||
fAcc[acc][i]->update(value); | |||
} | |||
} | |||
/** | |||
* Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter. | |||
* | |||
* @param p - the UI parameter index | |||
* @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping") | |||
* @param curve - between 0 and 3 | |||
* @param amin - mapping 'min' point | |||
* @param amid - mapping 'middle' point | |||
* @param amax - mapping 'max' point | |||
* | |||
*/ | |||
void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax) | |||
{ | |||
setConverter(fAcc, p, acc, curve, amin, amid, amax); | |||
} | |||
/** | |||
* Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter. | |||
* | |||
* @param p - the UI parameter index | |||
* @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping") | |||
* @param curve - between 0 and 3 | |||
* @param amin - mapping 'min' point | |||
* @param amid - mapping 'middle' point | |||
* @param amax - mapping 'max' point | |||
* | |||
*/ | |||
void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax) | |||
{ | |||
setConverter(fGyr, p, gyr, curve, amin, amid, amax); | |||
} | |||
/** | |||
* Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter. | |||
* | |||
* @param p - the UI parameter index | |||
* @param acc - the acc value to be retrieved (-1 means "no mapping") | |||
* @param curve - the curve value to be retrieved | |||
* @param amin - the amin value to be retrieved | |||
* @param amid - the amid value to be retrieved | |||
* @param amax - the amax value to be retrieved | |||
* | |||
*/ | |||
void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax) | |||
{ | |||
getConverter(fAcc, p, acc, curve, amin, amid, amax); | |||
} | |||
/** | |||
* Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter. | |||
* | |||
* @param p - the UI parameter index | |||
* @param gyr - the gyr value to be retrieved (-1 means "no mapping") | |||
* @param curve - the curve value to be retrieved | |||
* @param amin - the amin value to be retrieved | |||
* @param amid - the amid value to be retrieved | |||
* @param amax - the amax value to be retrieved | |||
* | |||
*/ | |||
void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax) | |||
{ | |||
getConverter(fGyr, p, gyr, curve, amin, amid, amax); | |||
} | |||
/** | |||
* Set a new value coming from an gyroscope, propagate it to all relevant float* zones. | |||
* | |||
* @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope | |||
* @param value - the new value | |||
* | |||
*/ | |||
void propagateGyr(int gyr, double value) | |||
{ | |||
for (int i = 0; i < fGyr[gyr].size(); i++) { | |||
fGyr[gyr][i]->update(value); | |||
} | |||
} | |||
// getScreenColor() : -1 means no screen color control (no screencolor metadata found) | |||
// otherwise return 0x00RRGGBB a ready to use color | |||
int getScreenColor() | |||
{ | |||
if (fHasScreenControl) { | |||
int r = (fRedReader) ? fRedReader->getValue() : 0; | |||
int g = (fGreenReader) ? fGreenReader->getValue() : 0; | |||
int b = (fBlueReader) ? fBlueReader->getValue() : 0; | |||
return (r<<16) | (g<<8) | b; | |||
} else { | |||
return -1; | |||
} | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,130 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef CONTROL_UI_H | |||
#define CONTROL_UI_H | |||
#include "faust/gui/UI.h" | |||
#include <jack/midiport.h> | |||
#include <vector> | |||
#include <assert.h> | |||
class ControlUI : public UI { | |||
protected: | |||
std::vector<FAUSTFLOAT*> fControlIn; | |||
std::vector<FAUSTFLOAT*> fControlOut; | |||
// -- widget's layouts | |||
void openTabBox(const char* label) {} | |||
void openHorizontalBox(const char* label) {} | |||
void openVerticalBox(const char* label) {}; | |||
void closeBox() {} | |||
// -- active widgets | |||
void addButton(const char* label, FAUSTFLOAT* zone) { fControlIn.push_back(zone); } | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone) { fControlIn.push_back(zone); } | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) { fControlIn.push_back(zone); }; | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) { fControlIn.push_back(zone); }; | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) { fControlIn.push_back(zone); }; | |||
// -- passive widgets | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) { fControlOut.push_back(zone); }; | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) { fControlOut.push_back(zone); }; | |||
public: | |||
void encode_control(float* control_buffer, unsigned int frames) | |||
{ | |||
assert(fControlOut.size() <= frames); | |||
for (unsigned int i = 0; i < fControlOut.size(); i++) { | |||
control_buffer[i] = *fControlOut[i]; | |||
} | |||
} | |||
void decode_control(float* control_buffer, unsigned int frames) | |||
{ | |||
assert(fControlIn.size() <= frames); | |||
for (unsigned int i = 0; i < fControlIn.size(); i++) { | |||
*fControlIn[i] = control_buffer[i]; | |||
} | |||
} | |||
void encode_midi_control(void* midi_control_buffer, unsigned int frames) | |||
{ | |||
assert(fControlOut.size() <= frames); | |||
jack_midi_reset_buffer(midi_control_buffer); | |||
for (unsigned int i = 0; i < fControlOut.size(); i++) { | |||
jack_midi_data_t* buffer = jack_midi_event_reserve(midi_control_buffer, i, 4); | |||
assert(buffer); | |||
*((float*)buffer) = *fControlOut[i]; | |||
} | |||
} | |||
static void encode_midi_control(void* midi_control_buffer, float* control_buffer, int count) | |||
{ | |||
jack_midi_reset_buffer(midi_control_buffer); | |||
for (unsigned int i = 0; i < count; i++) { | |||
jack_midi_data_t* buffer = jack_midi_event_reserve(midi_control_buffer, i, 4); | |||
assert(buffer); | |||
*((float*)buffer) = control_buffer[i]; | |||
} | |||
} | |||
void decode_midi_control(void* midi_control_buffer, unsigned int frames) | |||
{ | |||
assert(jack_midi_get_event_count(midi_control_buffer) <= frames); | |||
for (int i = 0; i < jack_midi_get_event_count(midi_control_buffer); i++) { | |||
jack_midi_event_t in_event; | |||
jack_midi_event_get(&in_event, midi_control_buffer, i); | |||
*fControlIn[i] = *((float*)in_event.buffer); | |||
} | |||
} | |||
static void decode_midi_control(void* midi_control_buffer, float* control_buffer, int count) | |||
{ | |||
assert(jack_midi_get_event_count(midi_control_buffer) <= count); | |||
for (int i = 0; i < jack_midi_get_event_count(midi_control_buffer); i++) { | |||
jack_midi_event_t in_event; | |||
jack_midi_event_get(&in_event, midi_control_buffer, i); | |||
control_buffer[i] = *((float*)(in_event.buffer)); | |||
} | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,181 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef FAUST_FUI_H | |||
#define FAUST_FUI_H | |||
#include "faust/gui/UI.h" | |||
#include <string> | |||
#include <map> | |||
#include <set> | |||
#include <vector> | |||
#include <stack> | |||
#include <iostream> | |||
#include <fstream> | |||
#if 1 | |||
/******************************************************************************* | |||
* FUI : used to save and recall the state of the user interface | |||
* This class provides essentially two new methods saveState() and recallState() | |||
* used to save on file and recall from file the state of the user interface. | |||
* The file is human readable and editable | |||
******************************************************************************/ | |||
class FUI : public UI | |||
{ | |||
protected: | |||
std::stack<std::string> fGroupStack; | |||
std::vector<std::string> fNameList; | |||
std::map<std::string, FAUSTFLOAT*> fName2Zone; | |||
std::map<FAUSTFLOAT*, bool> fButtons; | |||
// labels are normalized by replacing white spaces by underscores and by removing parenthesis | |||
std::string normalizeLabel(const char* label) | |||
{ | |||
std::string s; | |||
char c; | |||
while ((c = *label++)) { | |||
if (isspace(c)) { s += '_'; } | |||
//else if ((c == '(') | (c == ')') ) { } | |||
else { s += c; } | |||
} | |||
return s; | |||
} | |||
// add an element by relating its full name and memory zone | |||
virtual void addElement(const char* label, FAUSTFLOAT* zone, bool button = false) | |||
{ | |||
std::string fullname (fGroupStack.top() + '/' + normalizeLabel(label)); | |||
fNameList.push_back(fullname); | |||
fName2Zone[fullname] = zone; | |||
fButtons[zone] = button; | |||
} | |||
// keep track of full group names in a stack | |||
virtual void pushGroupLabel(const char* label) | |||
{ | |||
if (fGroupStack.empty()) { | |||
fGroupStack.push(normalizeLabel(label)); | |||
} else { | |||
fGroupStack.push(fGroupStack.top() + '/' + normalizeLabel(label)); | |||
} | |||
} | |||
virtual void popGroupLabel() | |||
{ | |||
fGroupStack.pop(); | |||
} | |||
public: | |||
FUI() {} | |||
virtual ~FUI() {} | |||
// -- Save and recall methods | |||
// save the zones values and full names | |||
virtual void saveState(const char* filename) | |||
{ | |||
std::ofstream f(filename); | |||
for (unsigned int i = 0; i < fNameList.size(); i++) { | |||
std::string n = fNameList[i]; | |||
FAUSTFLOAT* z = fName2Zone[n]; | |||
f << *z << ' ' << n.c_str() << std::endl; | |||
} | |||
f << std::endl; | |||
f.close(); | |||
} | |||
// recall the zones values and full names | |||
virtual void recallState(const char* filename) | |||
{ | |||
std::ifstream f(filename); | |||
FAUSTFLOAT v; | |||
std::string n; | |||
while (f.good()) { | |||
f >> v >> n; | |||
if (fName2Zone.count(n) > 0) { | |||
*(fName2Zone[n]) = v; | |||
} else if (n.size() > 0) { | |||
std::cerr << "recallState : parameter not found : " << n << " with value : " << v << std::endl; | |||
} | |||
} | |||
f.close(); | |||
} | |||
void setButtons(bool state) | |||
{ | |||
std::map<FAUSTFLOAT*, bool>::iterator it; | |||
for (it = fButtons.begin(); it != fButtons.end(); it++) { | |||
FAUSTFLOAT* zone = (*it).first; | |||
if ((*it).second) { | |||
*zone = state; | |||
} | |||
} | |||
} | |||
// -- widget's layouts (just keep track of group labels) | |||
virtual void openTabBox(const char* label) { pushGroupLabel(label); } | |||
virtual void openHorizontalBox(const char* label) { pushGroupLabel(label); } | |||
virtual void openVerticalBox(const char* label) { pushGroupLabel(label); } | |||
virtual void closeBox() { popGroupLabel(); }; | |||
// -- active widgets (just add an element) | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) { addElement(label, zone, true); } | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) { addElement(label, zone); } | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT , FAUSTFLOAT , FAUSTFLOAT , FAUSTFLOAT) | |||
{ addElement(label, zone); } | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT , FAUSTFLOAT , FAUSTFLOAT , FAUSTFLOAT) | |||
{ addElement(label, zone); } | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT , FAUSTFLOAT , FAUSTFLOAT , FAUSTFLOAT) | |||
{ addElement(label, zone); } | |||
// -- passive widgets (are ignored) | |||
virtual void addHorizontalBargraph(const char*, FAUSTFLOAT*, FAUSTFLOAT, FAUSTFLOAT) {}; | |||
virtual void addVerticalBargraph(const char*, FAUSTFLOAT*, FAUSTFLOAT, FAUSTFLOAT) {}; | |||
// -- metadata are not used | |||
virtual void declare(FAUSTFLOAT*, const char*, const char*) {} | |||
}; | |||
#endif | |||
#endif | |||
@@ -0,0 +1,261 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef FAUST_GUI_H | |||
#define FAUST_GUI_H | |||
#include "faust/gui/UI.h" | |||
#include "faust/gui/ring-buffer.h" | |||
#include <list> | |||
#include <map> | |||
#include <vector> | |||
/******************************************************************************* | |||
* GUI : Abstract Graphic User Interface | |||
* Provides additional mechanisms to synchronize widgets and zones. Widgets | |||
* should both reflect the value of a zone and allow to change this value. | |||
******************************************************************************/ | |||
class uiItem; | |||
typedef void (*uiCallback)(FAUSTFLOAT val, void* data); | |||
class clist : public std::list<uiItem*> | |||
{ | |||
public: | |||
virtual ~clist(); | |||
}; | |||
typedef std::map<FAUSTFLOAT*, clist*> zmap; | |||
typedef std::map<FAUSTFLOAT*, ringbuffer_t*> ztimedmap; | |||
class GUI : public UI | |||
{ | |||
private: | |||
static std::list<GUI*> fGuiList; | |||
zmap fZoneMap; | |||
bool fStopped; | |||
public: | |||
GUI() : fStopped(false) | |||
{ | |||
fGuiList.push_back(this); | |||
} | |||
virtual ~GUI() | |||
{ | |||
// delete all | |||
zmap::iterator g; | |||
for (g = fZoneMap.begin(); g != fZoneMap.end(); g++) { | |||
delete (*g).second; | |||
} | |||
// suppress 'this' in static fGuiList | |||
fGuiList.remove(this); | |||
} | |||
// -- registerZone(z,c) : zone management | |||
void registerZone(FAUSTFLOAT* z, uiItem* c) | |||
{ | |||
if (fZoneMap.find(z) == fZoneMap.end()) fZoneMap[z] = new clist(); | |||
fZoneMap[z]->push_back(c); | |||
} | |||
void updateAllZones(); | |||
void updateZone(FAUSTFLOAT* z); | |||
static void updateAllGuis() | |||
{ | |||
std::list<GUI*>::iterator g; | |||
for (g = fGuiList.begin(); g != fGuiList.end(); g++) { | |||
(*g)->updateAllZones(); | |||
} | |||
} | |||
void addCallback(FAUSTFLOAT* zone, uiCallback foo, void* data); | |||
virtual void show() {}; | |||
virtual bool run() { return false; }; | |||
virtual void stop() { fStopped = true; } | |||
bool stopped() { return fStopped; } | |||
virtual void declare(FAUSTFLOAT* , const char* , const char*) {} | |||
// Static global for timed zones, shared between all UI that will set timed values | |||
static ztimedmap gTimedZoneMap; | |||
}; | |||
/** | |||
* User Interface Item: abstract definition | |||
*/ | |||
class uiItem | |||
{ | |||
protected: | |||
GUI* fGUI; | |||
FAUSTFLOAT* fZone; | |||
FAUSTFLOAT fCache; | |||
uiItem(GUI* ui, FAUSTFLOAT* zone) : fGUI(ui), fZone(zone), fCache(FAUSTFLOAT(-123456.654321)) | |||
{ | |||
ui->registerZone(zone, this); | |||
} | |||
public: | |||
virtual ~uiItem() | |||
{} | |||
void modifyZone(FAUSTFLOAT v) | |||
{ | |||
fCache = v; | |||
if (*fZone != v) { | |||
*fZone = v; | |||
fGUI->updateZone(fZone); | |||
} | |||
} | |||
FAUSTFLOAT cache() { return fCache; } | |||
virtual void reflectZone() = 0; | |||
}; | |||
/** | |||
* Callback Item | |||
*/ | |||
struct uiCallbackItem : public uiItem | |||
{ | |||
uiCallback fCallback; | |||
void* fData; | |||
uiCallbackItem(GUI* ui, FAUSTFLOAT* zone, uiCallback foo, void* data) | |||
: uiItem(ui, zone), fCallback(foo), fData(data) {} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
fCallback(v, fData); | |||
} | |||
}; | |||
/** | |||
* Allows to group a set of zones. | |||
*/ | |||
class uiGroupItem : public uiItem | |||
{ | |||
protected: | |||
std::vector<FAUSTFLOAT*> fZoneMap; | |||
public: | |||
uiGroupItem(GUI* ui, FAUSTFLOAT* zone):uiItem(ui, zone) | |||
{} | |||
virtual ~uiGroupItem() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
// Update all zones of the same group | |||
std::vector<FAUSTFLOAT*>::iterator it; | |||
for (it = fZoneMap.begin(); it != fZoneMap.end(); it++) { | |||
(*(*it)) = v; | |||
} | |||
} | |||
void addZone(FAUSTFLOAT* zone) { fZoneMap.push_back(zone); } | |||
}; | |||
// en cours d'installation de callback : a finir!!!!! | |||
/** | |||
* Update all user items reflecting zone z | |||
*/ | |||
inline void GUI::updateZone(FAUSTFLOAT* z) | |||
{ | |||
FAUSTFLOAT v = *z; | |||
clist* l = fZoneMap[z]; | |||
for (clist::iterator c = l->begin(); c != l->end(); c++) { | |||
if ((*c)->cache() != v) (*c)->reflectZone(); | |||
} | |||
} | |||
/** | |||
* Update all user items not up to date | |||
*/ | |||
inline void GUI::updateAllZones() | |||
{ | |||
for (zmap::iterator m = fZoneMap.begin(); m != fZoneMap.end(); m++) { | |||
FAUSTFLOAT* z = m->first; | |||
clist* l = m->second; | |||
if (z) { | |||
FAUSTFLOAT v = *z; | |||
for (clist::iterator c = l->begin(); c != l->end(); c++) { | |||
if ((*c)->cache() != v) (*c)->reflectZone(); | |||
} | |||
} | |||
} | |||
} | |||
inline void GUI::addCallback(FAUSTFLOAT* zone, uiCallback foo, void* data) | |||
{ | |||
new uiCallbackItem(this, zone, foo, data); | |||
}; | |||
inline clist::~clist() | |||
{ | |||
std::list<uiItem*>::iterator it; | |||
for (it = begin(); it != end(); it++) { | |||
delete (*it); | |||
} | |||
} | |||
// For precise timestamped control | |||
struct DatedControl { | |||
double fDate; | |||
FAUSTFLOAT fValue; | |||
DatedControl(double d = 0., FAUSTFLOAT v = FAUSTFLOAT(0)):fDate(d), fValue(v) {} | |||
}; | |||
#endif |
@@ -0,0 +1,97 @@ | |||
/* | |||
Faust Project | |||
Copyright (C) 2012 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __HTTPDControler__ | |||
#define __HTTPDControler__ | |||
#include <string> | |||
#include <map> | |||
namespace httpdfaust | |||
{ | |||
class HTTPDSetup; | |||
class JSONDesc; | |||
class FaustFactory; | |||
class jsonfactory; | |||
class htmlfactory; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief the main Faust HTTPD Lib API | |||
The HTTPDControler is essentially a glue between the memory representation (in charge of the FaustFactory), | |||
and the network services (in charge of HTTPDSetup). | |||
*/ | |||
class HTTPDControler | |||
{ | |||
int fTCPPort; // the tcp port number | |||
FaustFactory* fFactory; // a factory to build the memory representation | |||
jsonfactory* fJson; | |||
htmlfactory* fHtml; | |||
HTTPDSetup* fHttpd; // the network manager | |||
std::string fHTML; // the corresponding HTML page | |||
std::map<std::string, std::string> fCurrentMeta; // the current meta declarations | |||
bool fInit; | |||
public: | |||
/* | |||
base udp port is chosen in an unassigned range from IANA PORT NUMBERS (last updated 2011-01-24) | |||
see at http://www.iana.org/assignments/port-numbers | |||
5507-5552 Unassigned | |||
*/ | |||
enum { kTCPBasePort = 5510}; | |||
HTTPDControler(int argc, char *argv[], const char* applicationname, bool init = true); | |||
virtual ~HTTPDControler(); | |||
//-------------------------------------------------------------------------- | |||
// addnode, opengroup and closegroup are simply relayed to the factory | |||
//-------------------------------------------------------------------------- | |||
template <typename C> void addnode(const char* type, const char* label, C* zone); | |||
template <typename C> void addnode(const char* type, const char* label, C* zone, C min, C max); | |||
template <typename C> void addnode(const char* type, const char* label, C* zone, C init, C min, C max, C step); | |||
void declare(const char* key, const char* val ) { fCurrentMeta[key] = val; } | |||
void opengroup(const char* type, const char* label); | |||
void closegroup(); | |||
//-------------------------------------------------------------------------- | |||
void run(); // start the httpd server | |||
void stop(); // stop the httpd server | |||
int getTCPPort() { return fTCPPort; } | |||
std::string getJSON(); | |||
void setInputs(int numInputs); | |||
void setOutputs(int numOutputs); | |||
static float version(); // the Faust httpd library version number | |||
static const char* versionstr(); // the Faust httpd library version number as a string | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,318 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef FAUST_JSONUI_H | |||
#define FAUST_JSONUI_H | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
#include "faust/gui/UI.h" | |||
#include "faust/gui/PathBuilder.h" | |||
#include "faust/gui/meta.h" | |||
#include <vector> | |||
#include <map> | |||
#include <string> | |||
#include <iostream> | |||
#include <sstream> | |||
#include <assert.h> | |||
/******************************************************************************* | |||
* JSONUI : Faust User Interface | |||
* This class produce a complete JSON decription of the DSP instance. | |||
******************************************************************************/ | |||
class JSONUI : public PathBuilder, public Meta, public UI | |||
{ | |||
protected: | |||
std::stringstream fJSON; | |||
std::stringstream fUI; | |||
std::stringstream fMeta; | |||
std::vector<std::pair <std::string, std::string> > fMetaAux; | |||
std::string fName; | |||
std::string fExpandedCode; | |||
std::string fSHAKey; | |||
char fCloseUIPar; | |||
char fCloseMetaPar; | |||
int fTab; | |||
int fInputs, fOutputs; | |||
void tab(int n, std::ostream& fout) | |||
{ | |||
fout << '\n'; | |||
while (n-- > 0) { | |||
fout << '\t'; | |||
} | |||
} | |||
void addMeta(int tab_val, bool quote = true) | |||
{ | |||
if (fMetaAux.size() > 0) { | |||
tab(tab_val, fUI); fUI << "\"meta\": ["; | |||
std::string sep = ""; | |||
for (size_t i = 0; i < fMetaAux.size(); i++) { | |||
fUI << sep; | |||
tab(tab_val + 1, fUI); fUI << "{ \"" << fMetaAux[i].first << "\": \"" << fMetaAux[i].second << "\" }"; | |||
sep = ","; | |||
} | |||
tab(tab_val, fUI); fUI << ((quote) ? "],": "]"); | |||
fMetaAux.clear(); | |||
} | |||
} | |||
void init(const std::string& name, int inputs, int outputs, const std::string& sha_key, const std::string& dsp_code) | |||
{ | |||
fTab = 1; | |||
// Start Meta generation | |||
tab(fTab, fMeta); fMeta << "\"meta\": ["; | |||
fCloseMetaPar = ' '; | |||
// Start UI generation | |||
tab(fTab, fUI); fUI << "\"ui\": ["; | |||
fCloseUIPar = ' '; | |||
fTab += 1; | |||
fName = name; | |||
fInputs = inputs; | |||
fOutputs = outputs; | |||
fExpandedCode = dsp_code; | |||
fSHAKey = sha_key; | |||
} | |||
inline std::string flatten(const std::string& src) | |||
{ | |||
std::stringstream dst; | |||
for (size_t i = 0; i < src.size(); i++) { | |||
switch (src[i]) { | |||
case '\n': | |||
case '\t': | |||
dst << ' '; | |||
break; | |||
case '"': | |||
dst << "\\" << '"'; | |||
break; | |||
default: | |||
dst << src[i]; | |||
break; | |||
} | |||
} | |||
return dst.str(); | |||
} | |||
public: | |||
JSONUI(const std::string& name, int inputs, int outputs, const std::string& sha_key, const std::string& dsp_code) | |||
{ | |||
init(name, inputs, outputs, sha_key, dsp_code); | |||
} | |||
JSONUI(const std::string& name, int inputs, int outputs) | |||
{ | |||
init(name, inputs, outputs, "", ""); | |||
} | |||
JSONUI(int inputs, int outputs) | |||
{ | |||
init("", inputs, outputs, "", ""); | |||
} | |||
JSONUI() | |||
{ | |||
init("", -1, -1, "", ""); | |||
} | |||
virtual ~JSONUI() {} | |||
// -- widget's layouts | |||
virtual void openGenericGroup(const char* label, const char* name) | |||
{ | |||
fControlsLevel.push_back(label); | |||
fUI << fCloseUIPar; | |||
tab(fTab, fUI); fUI << "{"; | |||
fTab += 1; | |||
tab(fTab, fUI); fUI << "\"type\": \"" << name << "\","; | |||
tab(fTab, fUI); fUI << "\"label\": \"" << label << "\","; | |||
addMeta(fTab + 1); | |||
tab(fTab, fUI); fUI << "\"items\": ["; | |||
fCloseUIPar = ' '; | |||
fTab += 1; | |||
} | |||
virtual void openTabBox(const char* label) | |||
{ | |||
openGenericGroup(label, "tgroup"); | |||
} | |||
virtual void openHorizontalBox(const char* label) | |||
{ | |||
openGenericGroup(label, "hgroup"); | |||
} | |||
virtual void openVerticalBox(const char* label) | |||
{ | |||
openGenericGroup(label, "vgroup"); | |||
} | |||
virtual void closeBox() | |||
{ | |||
fControlsLevel.pop_back(); | |||
fTab -= 1; | |||
tab(fTab, fUI); fUI << "]"; | |||
fTab -= 1; | |||
tab(fTab, fUI); fUI << "}"; | |||
fCloseUIPar = ','; | |||
} | |||
// -- active widgets | |||
virtual void addGenericButton(const char* label, const char* name) | |||
{ | |||
fUI << fCloseUIPar; | |||
tab(fTab, fUI); fUI << "{"; | |||
tab(fTab + 1, fUI); fUI << "\"type\": \"" << name << "\","; | |||
tab(fTab + 1, fUI); fUI << "\"label\": \"" << label << "\"" << ","; | |||
tab(fTab + 1, fUI); fUI << "\"address\": \"" << buildPath(label) << "\"" << ((fMetaAux.size() > 0) ? "," : ""); | |||
addMeta(fTab + 1, false); | |||
tab(fTab, fUI); fUI << "}"; | |||
fCloseUIPar = ','; | |||
} | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
addGenericButton(label, "button"); | |||
} | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
addGenericButton(label, "checkbox"); | |||
} | |||
virtual void addGenericEntry(const char* label, const char* name, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
fUI << fCloseUIPar; | |||
tab(fTab, fUI); fUI << "{"; | |||
tab(fTab + 1, fUI); fUI << "\"type\": \"" << name << "\","; | |||
tab(fTab + 1, fUI); fUI << "\"label\": \"" << label << "\"" << ","; | |||
tab(fTab + 1, fUI); fUI << "\"address\": \"" << buildPath(label) << "\"" << ","; | |||
addMeta(fTab + 1); | |||
tab(fTab + 1, fUI); fUI << "\"init\": \"" << init << "\","; | |||
tab(fTab + 1, fUI); fUI << "\"min\": \"" << min << "\","; | |||
tab(fTab + 1, fUI); fUI << "\"max\": \"" << max << "\","; | |||
tab(fTab + 1, fUI); fUI << "\"step\": \"" << step << "\""; | |||
tab(fTab, fUI); fUI << "}"; | |||
fCloseUIPar = ','; | |||
} | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addGenericEntry(label, "vslider", init, min, max, step); | |||
} | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addGenericEntry(label, "hslider", init, min, max, step); | |||
} | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addGenericEntry(label, "nentry", init, min, max, step); | |||
} | |||
// -- passive widgets | |||
virtual void addGenericBargraph(const char* label, const char* name, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
fUI << fCloseUIPar; | |||
tab(fTab, fUI); fUI << "{"; | |||
tab(fTab + 1, fUI); fUI << "\"type\": \"" << name << "\","; | |||
tab(fTab + 1, fUI); fUI << "\"label\": \"" << label << "\"" << ","; | |||
tab(fTab + 1, fUI); fUI << "\"address\": \"" << buildPath(label) << "\"" << ","; | |||
addMeta(fTab + 1); | |||
tab(fTab + 1, fUI); fUI << "\"min\": \"" << min << "\","; | |||
tab(fTab + 1, fUI); fUI << "\"max\": \"" << max << "\""; | |||
tab(fTab, fUI); fUI << "}"; | |||
fCloseUIPar = ','; | |||
} | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
addGenericBargraph(label, "hbargraph", min, max); | |||
} | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
addGenericBargraph(label, "vbargraph", min, max); | |||
} | |||
// -- metadata declarations | |||
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{ | |||
fMetaAux.push_back(std::make_pair(key, val)); | |||
} | |||
// Meta interface | |||
virtual void declare(const char* key, const char* value) | |||
{ | |||
fMeta << fCloseMetaPar; | |||
if ((strcmp(key, "name") == 0) && (fName == "")) fName = value; | |||
tab(fTab, fMeta); fMeta << "{ " << "\"" << key << "\"" << ": " << "\"" << value << "\" }"; | |||
fCloseMetaPar = ','; | |||
} | |||
std::string JSON(bool flat = false) | |||
{ | |||
fTab = 0; | |||
fJSON << "{"; | |||
fTab += 1; | |||
tab(fTab, fJSON); fJSON << "\"name\": \"" << fName << "\","; | |||
if (fSHAKey != "") { tab(fTab, fJSON); fJSON << "\"sha_key\": \"" << fSHAKey << "\","; } | |||
if (fExpandedCode != "") { tab(fTab, fJSON); fJSON << "\"code\": \"" << fExpandedCode << "\","; } | |||
tab(fTab, fJSON); fJSON << "\"inputs\": \"" << fInputs << "\","; | |||
tab(fTab, fJSON); fJSON << "\"outputs\": \"" << fOutputs << "\","; | |||
tab(fTab, fMeta); fMeta << "],"; | |||
tab(fTab, fUI); fUI << "]"; | |||
fTab -= 1; | |||
if (fCloseMetaPar == ',') { // If "declare" has been called, fCloseMetaPar state is now ',' | |||
fJSON << fMeta.str() << fUI.str(); | |||
} else { | |||
fJSON << fUI.str(); | |||
} | |||
tab(fTab, fJSON); fJSON << "}" << std::endl; | |||
return (flat) ? flatten(fJSON.str()) : fJSON.str(); | |||
} | |||
}; | |||
#endif // FAUST_JSONUI_H |
@@ -0,0 +1,164 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __juce_osc__ | |||
#define __juce_osc__ | |||
#include "../JuceLibraryCode/JuceHeader.h" | |||
#include "faust/gui/MapUI.h" | |||
#include "faust/gui/GUI.h" | |||
#include "faust/gui/APIUI.h" | |||
class oscItem : public uiItem { | |||
protected: | |||
OSCSender* fSender; | |||
String fPath; | |||
public: | |||
oscItem(OSCSender* sender, GUI* ui, const String& path, FAUSTFLOAT* zone) | |||
:uiItem(ui, zone), fSender(sender), fPath(path) {} | |||
virtual ~oscItem() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
fSender->send(fPath, v); | |||
} | |||
}; | |||
class JuceOSCUI : private OSCReceiver, private OSCReceiver::Listener<OSCReceiver::RealtimeCallback>, public GUI { | |||
private: | |||
OSCSender fSender; | |||
String fIP; | |||
int fInputPort, fOutputPort; | |||
APIUI fAPIUI; | |||
Array<oscItem*> fOSCItems; // Pointers are kept and desallocated by the GUI class | |||
public: | |||
JuceOSCUI(const std::string& ip, int in_port, int out_port) | |||
:fIP(ip), fInputPort(in_port), fOutputPort(out_port) | |||
{} | |||
virtual ~JuceOSCUI() | |||
{} | |||
void oscMessageReceived(const OSCMessage& message) override | |||
{ | |||
String address = message.getAddressPattern().toString(); | |||
for (int i = 0; i < message.size(); ++i) { | |||
if (message[i].isFloat32()) { | |||
fAPIUI.setParamValue(fAPIUI.getParamIndex(address.toStdString().c_str()), message[i].getFloat32()); | |||
// "get" message with correct address | |||
} else if (message[i].isString() | |||
&& message[i].getString().equalsIgnoreCase("get") | |||
&& String(fAPIUI.getParamAddress(0)).startsWith(address)) { | |||
for (int p = 0; p < fAPIUI.getParamsCount(); ++p) { | |||
fSender.send(fAPIUI.getParamAddress(p), fAPIUI.getParamValue(p), fAPIUI.getParamMin(p), fAPIUI.getParamMax(p)); | |||
} | |||
// "hello" message | |||
} else if (message[i].isString() | |||
&& address.equalsIgnoreCase("/*") | |||
&& message[i].getString().equalsIgnoreCase("hello")) { | |||
String path = fAPIUI.getParamAddress(0); | |||
int pos1 = path.indexOfChar('/'); | |||
int pos2 = path.indexOfChar(pos1 + 1, '/'); | |||
fSender.send(path.substring(pos1, pos2), fIP, fInputPort, fOutputPort); | |||
} | |||
} | |||
} | |||
bool run() override | |||
{ | |||
// Keep all zones for update when OSC messages are received | |||
if (fOSCItems.size() == 0) { | |||
for (int p = 0; p < fAPIUI.getParamsCount(); ++p) { | |||
fOSCItems.add(new oscItem(&fSender, this, fAPIUI.getParamAddress(p), fAPIUI.getParamZone(p))); | |||
} | |||
} | |||
if (!fSender.connect(fIP, fOutputPort)) { | |||
std::cerr << "Error: could not connect to UDP port " << fInputPort << std::endl; | |||
return false; | |||
} | |||
if (!connect(fInputPort)) { | |||
std::cerr << "Error: could not connect to UDP port " << fOutputPort << std::endl; | |||
return false; | |||
} | |||
addListener(this); | |||
return true; | |||
} | |||
void stop() override | |||
{ | |||
fSender.disconnect(); | |||
disconnect(); | |||
removeListener(this); | |||
} | |||
// -- widget's layouts | |||
void openTabBox(const char* label) override { fAPIUI.openTabBox(label); } | |||
void openHorizontalBox(const char* label) override { fAPIUI.openHorizontalBox(label); } | |||
void openVerticalBox(const char* label) override { fAPIUI.openVerticalBox(label); } | |||
void closeBox() override { fAPIUI.closeBox(); } | |||
// -- active widgets | |||
void addButton(const char* label, FAUSTFLOAT* zone) override { fAPIUI.addButton(label, zone); } | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone) override { fAPIUI.addCheckButton(label, zone); } | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override | |||
{ fAPIUI.addVerticalSlider(label, zone, init, min, max, step); } | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override | |||
{ fAPIUI.addHorizontalSlider(label, zone, init, min, max, step); } | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) override | |||
{ fAPIUI.addNumEntry(label, zone, init, min, max, step); } | |||
// -- passive widgets | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override | |||
{ fAPIUI.addHorizontalBargraph(label, zone, min, max); } | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) override | |||
{ fAPIUI.addVerticalBargraph(label, zone, min, max); } | |||
// -- metadata declarations | |||
void declare(FAUSTFLOAT* zone, const char* key, const char* val) override { fAPIUI.declare(zone, key, val); } | |||
}; | |||
#endif // __juce_osc__ | |||
@@ -0,0 +1,157 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef FAUST_MAPUI_H | |||
#define FAUST_MAPUI_H | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
#include <vector> | |||
#include <map> | |||
#include <string> | |||
#include "faust/gui/UI.h" | |||
#include "faust/gui/PathBuilder.h" | |||
/******************************************************************************* | |||
* MapUI : Faust User Interface | |||
* This class creates a map of complete hierarchical path and zones for each UI items. | |||
******************************************************************************/ | |||
class MapUI : public UI, public PathBuilder | |||
{ | |||
protected: | |||
// Complete path map | |||
std::map<std::string, FAUSTFLOAT*> fPathZoneMap; | |||
// Label zone map | |||
std::map<std::string, FAUSTFLOAT*> fLabelZoneMap; | |||
public: | |||
MapUI() {}; | |||
virtual ~MapUI() {}; | |||
// -- widget's layouts | |||
void openTabBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
} | |||
void openHorizontalBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
} | |||
void openVerticalBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
} | |||
void closeBox() | |||
{ | |||
fControlsLevel.pop_back(); | |||
} | |||
// -- active widgets | |||
void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
fPathZoneMap[buildPath(label)] = zone; | |||
fLabelZoneMap[label] = zone; | |||
} | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
fPathZoneMap[buildPath(label)] = zone; | |||
fLabelZoneMap[label] = zone; | |||
} | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step) | |||
{ | |||
fPathZoneMap[buildPath(label)] = zone; | |||
fLabelZoneMap[label] = zone; | |||
} | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step) | |||
{ | |||
fPathZoneMap[buildPath(label)] = zone; | |||
fLabelZoneMap[label] = zone; | |||
} | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT fmin, FAUSTFLOAT fmax, FAUSTFLOAT step) | |||
{ | |||
fPathZoneMap[buildPath(label)] = zone; | |||
fLabelZoneMap[label] = zone; | |||
} | |||
// -- passive widgets | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax) | |||
{ | |||
fPathZoneMap[buildPath(label)] = zone; | |||
fLabelZoneMap[label] = zone; | |||
} | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT fmin, FAUSTFLOAT fmax) | |||
{ | |||
fPathZoneMap[buildPath(label)] = zone; | |||
fLabelZoneMap[label] = zone; | |||
} | |||
// -- metadata declarations | |||
void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{} | |||
// set/get | |||
void setParamValue(const std::string& path, FAUSTFLOAT value) | |||
{ | |||
if (fPathZoneMap.find(path) != fPathZoneMap.end()) { | |||
*fPathZoneMap[path] = value; | |||
} else if (fLabelZoneMap.find(path) != fLabelZoneMap.end()) { | |||
*fLabelZoneMap[path] = value; | |||
} | |||
} | |||
FAUSTFLOAT getParamValue(const std::string& path) | |||
{ | |||
if (fPathZoneMap.find(path) != fPathZoneMap.end()) { | |||
return *fPathZoneMap[path]; | |||
} else if (fLabelZoneMap.find(path) != fLabelZoneMap.end()) { | |||
return *fLabelZoneMap[path]; | |||
} else { | |||
return FAUSTFLOAT(0); | |||
} | |||
} | |||
// map access | |||
std::map<std::string, FAUSTFLOAT*>& getMap() { return fPathZoneMap; } | |||
int getParamsCount() { return fPathZoneMap.size(); } | |||
std::string getParamAddress(int index) | |||
{ | |||
std::map<std::string, FAUSTFLOAT*>::iterator it = fPathZoneMap.begin(); | |||
while (index-- > 0 && it++ != fPathZoneMap.end()) {} | |||
return (*it).first; | |||
} | |||
}; | |||
#endif // FAUST_MAPUI_H |
@@ -0,0 +1,343 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef MetaData_UI_H | |||
#define MetaData_UI_H | |||
#include <map> | |||
#include <set> | |||
#include <string> | |||
#include <assert.h> | |||
#include "faust/gui/SimpleParser.h" | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
static inline bool startWith(const std::string& str, const std::string& prefix) | |||
{ | |||
return (str.substr(0, prefix.size()) == prefix); | |||
} | |||
/** | |||
* Convert a dB value into a scale between 0 and 1 (following IEC standard ?) | |||
*/ | |||
static inline FAUSTFLOAT dB2Scale(FAUSTFLOAT dB) | |||
{ | |||
FAUSTFLOAT scale = 1.0; | |||
/*if (dB < -70.0f) | |||
scale = 0.0f; | |||
else*/ if (dB < -60.0) | |||
scale = (dB + 70.0) * 0.0025; | |||
else if (dB < -50.0) | |||
scale = (dB + 60.0) * 0.005 + 0.025; | |||
else if (dB < -40.0) | |||
scale = (dB + 50.0) * 0.0075 + 0.075; | |||
else if (dB < -30.0) | |||
scale = (dB + 40.0) * 0.015 + 0.15; | |||
else if (dB < -20.0) | |||
scale = (dB + 30.0) * 0.02 + 0.3; | |||
else if (dB < -0.001 || dB > 0.001) /* if (dB < 0.0) */ | |||
scale = (dB + 20.0f) * 0.025 + 0.5; | |||
return scale; | |||
} | |||
/******************************************************************************* | |||
* MetaDataUI : Common class for MetaData handling | |||
******************************************************************************/ | |||
//============================= BEGIN GROUP LABEL METADATA=========================== | |||
// Unlike widget's label, metadata inside group's label are not extracted directly by | |||
// the Faust compiler. Therefore they must be extracted within the architecture file | |||
//----------------------------------------------------------------------------------- | |||
class MetaDataUI { | |||
protected: | |||
std::string gGroupTooltip; | |||
std::map<FAUSTFLOAT*, FAUSTFLOAT> fGuiSize; // map widget zone with widget size coef | |||
std::map<FAUSTFLOAT*, std::string> fTooltip; // map widget zone with tooltip strings | |||
std::map<FAUSTFLOAT*, std::string> fUnit; // map widget zone to unit string (i.e. "dB") | |||
std::map<FAUSTFLOAT*, std::string> fRadioDescription; // map zone to {'low':440; ...; 'hi':1000.0} | |||
std::map<FAUSTFLOAT*, std::string> fMenuDescription; // map zone to {'low':440; ...; 'hi':1000.0} | |||
std::set<FAUSTFLOAT*> fKnobSet; // set of widget zone to be knobs | |||
std::set<FAUSTFLOAT*> fLedSet; // set of widget zone to be LEDs | |||
std::set<FAUSTFLOAT*> fNumSet; // set of widget zone to be numerical bargraphs | |||
std::set<FAUSTFLOAT*> fLogSet; // set of widget zone having a log UI scale | |||
std::set<FAUSTFLOAT*> fExpSet; // set of widget zone having an exp UI scale | |||
void clearMetadata() | |||
{ | |||
fGuiSize.clear(); | |||
fTooltip.clear(); | |||
fUnit.clear(); | |||
fRadioDescription.clear(); | |||
fMenuDescription.clear(); | |||
fKnobSet.clear(); | |||
fLedSet.clear(); | |||
fNumSet.clear(); | |||
fLogSet.clear(); | |||
fExpSet.clear(); | |||
} | |||
bool isKnob(FAUSTFLOAT* zone) | |||
{ | |||
return fKnobSet.count(zone) > 0; | |||
} | |||
bool isRadio(FAUSTFLOAT* zone) | |||
{ | |||
return fRadioDescription.count(zone) > 0; | |||
} | |||
bool isMenu(FAUSTFLOAT* zone) | |||
{ | |||
return fMenuDescription.count(zone) > 0; | |||
} | |||
bool isLed(FAUSTFLOAT* zone) | |||
{ | |||
return fLedSet.count(zone) > 0; | |||
} | |||
bool isNumerical(FAUSTFLOAT* zone) | |||
{ | |||
return fNumSet.count(zone) > 0; | |||
} | |||
/** | |||
* rmWhiteSpaces(): Remove the leading and trailing white spaces of a string | |||
* (but not those in the middle of the string) | |||
*/ | |||
std::string rmWhiteSpaces(const std::string& s) | |||
{ | |||
size_t i = s.find_first_not_of(" \t"); | |||
size_t j = s.find_last_not_of(" \t"); | |||
if ((i != std::string::npos) && (j != std::string::npos)) { | |||
return s.substr(i, 1+j-i); | |||
} else { | |||
return ""; | |||
} | |||
} | |||
/** | |||
* Extracts metdata from a label : 'vol [unit: dB]' -> 'vol' + metadata(unit=dB) | |||
*/ | |||
void extractMetadata(const std::string& fulllabel, std::string& label, std::map<std::string, std::string>& metadata) | |||
{ | |||
enum {kLabel, kEscape1, kEscape2, kEscape3, kKey, kValue}; | |||
int state = kLabel; int deep = 0; | |||
std::string key, value; | |||
for (unsigned int i = 0; i < fulllabel.size(); i++) { | |||
char c = fulllabel[i]; | |||
switch (state) { | |||
case kLabel : | |||
assert(deep == 0); | |||
switch (c) { | |||
case '\\' : state = kEscape1; break; | |||
case '[' : state = kKey; deep++; break; | |||
default : label += c; | |||
} | |||
break; | |||
case kEscape1: | |||
label += c; | |||
state = kLabel; | |||
break; | |||
case kEscape2: | |||
key += c; | |||
state = kKey; | |||
break; | |||
case kEscape3: | |||
value += c; | |||
state = kValue; | |||
break; | |||
case kKey: | |||
assert(deep > 0); | |||
switch (c) { | |||
case '\\': | |||
state = kEscape2; | |||
break; | |||
case '[': | |||
deep++; | |||
key += c; | |||
break; | |||
case ':': | |||
if (deep == 1) { | |||
state = kValue; | |||
} else { | |||
key += c; | |||
} | |||
break; | |||
case ']': | |||
deep--; | |||
if (deep < 1) { | |||
metadata[rmWhiteSpaces(key)] = ""; | |||
state = kLabel; | |||
key=""; | |||
value=""; | |||
} else { | |||
key += c; | |||
} | |||
break; | |||
default : key += c; | |||
} | |||
break; | |||
case kValue: | |||
assert(deep > 0); | |||
switch (c) { | |||
case '\\': | |||
state = kEscape3; | |||
break; | |||
case '[': | |||
deep++; | |||
value += c; | |||
break; | |||
case ']': | |||
deep--; | |||
if (deep < 1) { | |||
metadata[rmWhiteSpaces(key)] = rmWhiteSpaces(value); | |||
state = kLabel; | |||
key = ""; | |||
value = ""; | |||
} else { | |||
value += c; | |||
} | |||
break; | |||
default : value += c; | |||
} | |||
break; | |||
default: | |||
std::cerr << "ERROR unrecognized state " << state << std::endl; | |||
} | |||
} | |||
label = rmWhiteSpaces(label); | |||
} | |||
/** | |||
* Format tooltip string by replacing some white spaces by | |||
* return characters so that line width doesn't exceed n. | |||
* Limitation : long words exceeding n are not cut | |||
*/ | |||
std::string formatTooltip(int n, const std::string& tt) | |||
{ | |||
std::string ss = tt; // ss string we are going to format | |||
int lws = 0; // last white space encountered | |||
int lri = 0; // last return inserted | |||
for (int i = 0; i < (int)tt.size(); i++) { | |||
if (tt[i] == ' ') lws = i; | |||
if (((i-lri) >= n) && (lws > lri)) { | |||
// insert return here | |||
ss[lws] = '\n'; | |||
lri = lws; | |||
} | |||
} | |||
return ss; | |||
} | |||
public: | |||
virtual ~MetaDataUI() | |||
{} | |||
enum Scale { | |||
kLin, | |||
kLog, | |||
kExp | |||
}; | |||
Scale getScale(FAUSTFLOAT* zone) | |||
{ | |||
if (fLogSet.count(zone) > 0) return kLog; | |||
if (fExpSet.count(zone) > 0) return kExp; | |||
return kLin; | |||
} | |||
/** | |||
* Analyses the widget zone metadata declarations and takes appropriate actions | |||
*/ | |||
void declare(FAUSTFLOAT* zone, const char* key, const char* value) | |||
{ | |||
if (zone == 0) { | |||
// special zone 0 means group metadata | |||
if (strcmp(key,"tooltip") == 0) { | |||
// only group tooltip are currently implemented | |||
gGroupTooltip = formatTooltip(30, value); | |||
} | |||
} else { | |||
if (strcmp(key,"size") == 0) { | |||
fGuiSize[zone] = atof(value); | |||
} | |||
else if (strcmp(key,"tooltip") == 0) { | |||
fTooltip[zone] = formatTooltip(30, value); | |||
} | |||
else if (strcmp(key,"unit") == 0) { | |||
fUnit[zone] = value ; | |||
} | |||
else if (strcmp(key,"scale") == 0) { | |||
if (strcmp(value,"log") == 0) { | |||
fLogSet.insert(zone); | |||
} else if (strcmp(value,"exp") == 0) { | |||
fExpSet.insert(zone); | |||
} | |||
} | |||
else if (strcmp(key,"style") == 0) { | |||
// else if ((strcmp(key,"style") == 0) || (strcmp(key,"type") == 0)) { | |||
if (strcmp(value,"knob") == 0) { | |||
fKnobSet.insert(zone); | |||
} else if (strcmp(value,"led") == 0) { | |||
fLedSet.insert(zone); | |||
} else if (strcmp(value,"numerical") == 0) { | |||
fNumSet.insert(zone); | |||
} else { | |||
const char* p = value; | |||
if (parseWord(p, "radio")) { | |||
fRadioDescription[zone] = std::string(p); | |||
} else if (parseWord(p, "menu")) { | |||
fMenuDescription[zone] = std::string(p); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,609 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef FAUST_MIDIUI_H | |||
#define FAUST_MIDIUI_H | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
#include <vector> | |||
#include <string> | |||
#include <utility> | |||
#include <iostream> | |||
#include "faust/gui/GUI.h" | |||
#include "faust/midi/midi.h" | |||
#include "faust/gui/ValueConverter.h" | |||
/******************************************************************************* | |||
* MidiUI : Faust User Interface | |||
* This class decodes MIDI meta data and maps incoming MIDI messages to them. | |||
* Currently "ctrl, keyon, keypress, pgm, chanpress, pitchwheel/pitchbend meta data is handled. | |||
******************************************************************************/ | |||
class uiMidiItem : public uiItem { | |||
protected: | |||
midi* fMidiOut; | |||
bool fInputCtrl; | |||
public: | |||
uiMidiItem(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input) | |||
:uiItem(ui, zone), fMidiOut(midi_out), fInputCtrl(input) {} | |||
virtual ~uiMidiItem() {} | |||
}; | |||
class uiMidiTimedItem : public uiMidiItem | |||
{ | |||
protected: | |||
bool fDelete; | |||
public: | |||
uiMidiTimedItem(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input = true) | |||
:uiMidiItem(midi_out, ui, zone, input) | |||
{ | |||
if (GUI::gTimedZoneMap.find(fZone) == GUI::gTimedZoneMap.end()) { | |||
GUI::gTimedZoneMap[fZone] = ringbuffer_create(8192); | |||
fDelete = true; | |||
} else { | |||
fDelete = false; | |||
} | |||
} | |||
virtual ~uiMidiTimedItem() | |||
{ | |||
ztimedmap::iterator it; | |||
if (fDelete && ((it = GUI::gTimedZoneMap.find(fZone)) != GUI::gTimedZoneMap.end())) { | |||
ringbuffer_free((*it).second); | |||
GUI::gTimedZoneMap.erase(it); | |||
} | |||
} | |||
void modifyZone(double date, FAUSTFLOAT v) | |||
{ | |||
size_t res; | |||
DatedControl dated_val(date, v); | |||
if ((res = ringbuffer_write(GUI::gTimedZoneMap[fZone], (const char*)&dated_val, sizeof(DatedControl))) != sizeof(DatedControl)) { | |||
std::cout << "ringbuffer_write error DatedControl" << std::endl; | |||
} | |||
} | |||
// TODO | |||
virtual void reflectZone() {} | |||
}; | |||
// MIDI sync | |||
class uiMidiStart : public uiMidiTimedItem | |||
{ | |||
public: | |||
uiMidiStart(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input = true) | |||
:uiMidiTimedItem(midi_out, ui, zone, input) | |||
{} | |||
virtual ~uiMidiStart() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
if (v != FAUSTFLOAT(0)) { | |||
fMidiOut->start_sync(0); | |||
} | |||
} | |||
}; | |||
class uiMidiStop : public uiMidiTimedItem | |||
{ | |||
public: | |||
uiMidiStop(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input = true) | |||
:uiMidiTimedItem(midi_out, ui, zone, input) | |||
{} | |||
virtual ~uiMidiStop() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
if (v != FAUSTFLOAT(1)) { | |||
fMidiOut->stop_sync(0); | |||
} | |||
} | |||
}; | |||
class uiMidiClock : public uiMidiTimedItem | |||
{ | |||
private: | |||
bool fState; | |||
public: | |||
uiMidiClock(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, bool input = true) | |||
:uiMidiTimedItem(midi_out, ui, zone, input), fState(false) | |||
{} | |||
virtual ~uiMidiClock() | |||
{} | |||
void modifyZone(double date, FAUSTFLOAT v) | |||
{ | |||
if (fInputCtrl) { | |||
fState = !fState; | |||
uiMidiTimedItem::modifyZone(date, FAUSTFLOAT(fState)); | |||
} | |||
} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
fMidiOut->clock(0); | |||
} | |||
}; | |||
class uiMidiProgChange : public uiMidiItem | |||
{ | |||
private: | |||
int fPgm; | |||
public: | |||
uiMidiProgChange(midi* midi_out, int pgm, GUI* ui, FAUSTFLOAT* zone, bool input = true) | |||
:uiMidiItem(midi_out, ui, zone, input), fPgm(pgm) | |||
{} | |||
virtual ~uiMidiProgChange() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
if (v != FAUSTFLOAT(0)) { | |||
fMidiOut->progChange(0, fPgm); | |||
} | |||
} | |||
}; | |||
class uiMidiChanPress : public uiMidiItem | |||
{ | |||
private: | |||
int fPress; | |||
public: | |||
uiMidiChanPress(midi* midi_out, int press, GUI* ui, FAUSTFLOAT* zone, bool input = true) | |||
:uiMidiItem(midi_out, ui, zone, input), fPress(press) | |||
{} | |||
virtual ~uiMidiChanPress() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
if (v != FAUSTFLOAT(0)) { | |||
fMidiOut->chanPress(0, fPress); | |||
} | |||
} | |||
}; | |||
class uiMidiCtrlChange : public uiMidiItem | |||
{ | |||
private: | |||
int fCtrl; | |||
LinearValueConverter fConverter; | |||
public: | |||
uiMidiCtrlChange(midi* midi_out, int ctrl, GUI* ui, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true) | |||
:uiMidiItem(midi_out, ui, zone, input), fCtrl(ctrl), fConverter(0., 127., double(min), double(max)) | |||
{} | |||
virtual ~uiMidiCtrlChange() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
fMidiOut->ctrlChange(0, fCtrl, fConverter.faust2ui(v)); | |||
} | |||
void modifyZone(int v) | |||
{ | |||
if (fInputCtrl) { | |||
uiItem::modifyZone(FAUSTFLOAT(fConverter.ui2faust(v))); | |||
} | |||
} | |||
}; | |||
class uiMidiPitchWheel : public uiMidiItem | |||
{ | |||
private: | |||
LinearValueConverter fConverter; | |||
public: | |||
uiMidiPitchWheel(midi* midi_out, GUI* ui, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true) | |||
:uiMidiItem(midi_out, ui, zone, input), fConverter(0., 16383, double(min), double(max)) | |||
{} | |||
virtual ~uiMidiPitchWheel() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
fMidiOut->pitchWheel(0, fConverter.faust2ui(v)); | |||
} | |||
void modifyZone(int v) | |||
{ | |||
if (fInputCtrl) { | |||
uiItem::modifyZone(FAUSTFLOAT(fConverter.ui2faust(v))); | |||
} | |||
} | |||
}; | |||
class uiMidiKeyOn : public uiMidiItem | |||
{ | |||
private: | |||
int fKeyOn; | |||
LinearValueConverter fConverter; | |||
public: | |||
uiMidiKeyOn(midi* midi_out, int key, GUI* ui, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true) | |||
:uiMidiItem(midi_out, ui, zone, input), fKeyOn(key), fConverter(0., 127., double(min), double(max)) | |||
{} | |||
virtual ~uiMidiKeyOn() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
fMidiOut->keyOn(0, fKeyOn, fConverter.faust2ui(v)); | |||
} | |||
void modifyZone(int v) | |||
{ | |||
if (fInputCtrl) { | |||
uiItem::modifyZone(FAUSTFLOAT(fConverter.ui2faust(v))); | |||
} | |||
} | |||
}; | |||
class uiMidiKeyOff : public uiMidiItem | |||
{ | |||
private: | |||
int fKeyOff; | |||
LinearValueConverter fConverter; | |||
public: | |||
uiMidiKeyOff(midi* midi_out, int key, GUI* ui, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true) | |||
:uiMidiItem(midi_out, ui, zone, input), fKeyOff(key), fConverter(0., 127., double(min), double(max)) | |||
{} | |||
virtual ~uiMidiKeyOff() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
fMidiOut->keyOff(0, fKeyOff, fConverter.faust2ui(v)); | |||
} | |||
void modifyZone(int v) | |||
{ | |||
if (fInputCtrl) { | |||
uiItem::modifyZone(FAUSTFLOAT(fConverter.ui2faust(v))); | |||
} | |||
} | |||
}; | |||
class uiMidiKeyPress : public uiMidiItem | |||
{ | |||
private: | |||
int fKeyOn; | |||
LinearValueConverter fConverter; | |||
public: | |||
uiMidiKeyPress(midi* midi_out, int key, GUI* ui, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true) | |||
:uiMidiItem(midi_out, ui, zone, input), fKeyOn(key), fConverter(0., 127., double(min), double(max)) | |||
{} | |||
virtual ~uiMidiKeyPress() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
fMidiOut->keyPress(0, fKeyOn, fConverter.faust2ui(v)); | |||
} | |||
void modifyZone(int v) | |||
{ | |||
if (fInputCtrl) { | |||
uiItem::modifyZone(FAUSTFLOAT(fConverter.ui2faust(v))); | |||
} | |||
} | |||
}; | |||
class MapUI; | |||
class MidiUI : public GUI, public midi | |||
{ | |||
protected: | |||
std::map <int, std::vector<uiMidiCtrlChange*> > fCtrlChangeTable; | |||
std::map <int, std::vector<uiMidiProgChange*> > fProgChangeTable; | |||
std::map <int, std::vector<uiMidiChanPress*> > fChanPressTable; | |||
std::map <int, std::vector<uiMidiKeyOn*> > fKeyOnTable; | |||
std::map <int, std::vector<uiMidiKeyOff*> > fKeyOffTable; | |||
std::map <int, std::vector<uiMidiKeyPress*> > fKeyPressTable; | |||
std::vector<uiMidiPitchWheel*> fPitchWheelTable; | |||
std::vector<uiMidiStart*> fStartTable; | |||
std::vector<uiMidiStop*> fStopTable; | |||
std::vector<uiMidiClock*> fClockTable; | |||
std::vector<std::pair <std::string, std::string> > fMetaAux; | |||
midi_handler* fMidiHandler; | |||
void addGenericZone(FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max, bool input = true) | |||
{ | |||
if (fMetaAux.size() > 0) { | |||
for (size_t i = 0; i < fMetaAux.size(); i++) { | |||
unsigned num; | |||
if (fMetaAux[i].first == "midi") { | |||
if (sscanf(fMetaAux[i].second.c_str(), "ctrl %u", &num) == 1) { | |||
fCtrlChangeTable[num].push_back(new uiMidiCtrlChange(fMidiHandler, num, this, zone, min, max, input)); | |||
} else if (sscanf(fMetaAux[i].second.c_str(), "keyon %u", &num) == 1) { | |||
fKeyOnTable[num].push_back(new uiMidiKeyOn(fMidiHandler, num, this, zone, min, max, input)); | |||
} else if (sscanf(fMetaAux[i].second.c_str(), "keyoff %u", &num) == 1) { | |||
fKeyOffTable[num].push_back(new uiMidiKeyOff(fMidiHandler, num, this, zone, min, max, input)); | |||
} else if (sscanf(fMetaAux[i].second.c_str(), "keypress %u", &num) == 1) { | |||
fKeyPressTable[num].push_back(new uiMidiKeyPress(fMidiHandler, num, this, zone, min, max, input)); | |||
} else if (sscanf(fMetaAux[i].second.c_str(), "pgm %u", &num) == 1) { | |||
fProgChangeTable[num].push_back(new uiMidiProgChange(fMidiHandler, num, this, zone, input)); | |||
} else if (sscanf(fMetaAux[i].second.c_str(), "chanpress %u", &num) == 1) { | |||
fChanPressTable[num].push_back(new uiMidiChanPress(fMidiHandler, num, this, zone, input)); | |||
} else if (strcmp(fMetaAux[i].second.c_str(), "pitchwheel") == 0 | |||
|| strcmp(fMetaAux[i].second.c_str(), "pitchbend") == 0) { | |||
fPitchWheelTable.push_back(new uiMidiPitchWheel(fMidiHandler, this, zone, min, max, input)); | |||
// MIDI sync | |||
} else if (strcmp(fMetaAux[i].second.c_str(), "start") == 0) { | |||
fStartTable.push_back(new uiMidiStart(fMidiHandler, this, zone, input)); | |||
} else if (strcmp(fMetaAux[i].second.c_str(), "stop") == 0) { | |||
fStopTable.push_back(new uiMidiStop(fMidiHandler, this, zone, input)); | |||
} else if (strcmp(fMetaAux[i].second.c_str(), "clock") == 0) { | |||
fClockTable.push_back(new uiMidiClock(fMidiHandler, this, zone, input)); | |||
} | |||
} | |||
} | |||
} | |||
fMetaAux.clear(); | |||
} | |||
public: | |||
MidiUI(midi_handler* midi_handler) | |||
{ | |||
fMidiHandler = midi_handler; | |||
fMidiHandler->addMidiIn(this); | |||
} | |||
virtual ~MidiUI() | |||
{ | |||
fMidiHandler->removeMidiIn(this); | |||
} | |||
bool run() { return fMidiHandler->start_midi(); } | |||
void stop() { fMidiHandler->stop_midi(); } | |||
void addMidiIn(midi* midi_dsp) { fMidiHandler->addMidiIn(midi_dsp); } | |||
void removeMidiIn(midi* midi_dsp) { fMidiHandler->removeMidiIn(midi_dsp); } | |||
// -- widget's layouts | |||
virtual void openTabBox(const char* label) | |||
{} | |||
virtual void openHorizontalBox(const char* label) | |||
{} | |||
virtual void openVerticalBox(const char* label) | |||
{} | |||
virtual void closeBox() | |||
{} | |||
// -- active widgets | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
addGenericZone(zone, FAUSTFLOAT(0), FAUSTFLOAT(1)); | |||
} | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
addGenericZone(zone, FAUSTFLOAT(0), FAUSTFLOAT(1)); | |||
} | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addGenericZone(zone, min, max); | |||
} | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addGenericZone(zone, min, max); | |||
} | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addGenericZone(zone, min, max); | |||
} | |||
// -- passive widgets | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
addGenericZone(zone, min, max, false); | |||
} | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
addGenericZone(zone, min, max, false); | |||
} | |||
// -- metadata declarations | |||
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{ | |||
fMetaAux.push_back(std::make_pair(key, val)); | |||
} | |||
// -- MIDI API | |||
MapUI* keyOn(double date, int channel, int note, int velocity) | |||
{ | |||
if (fKeyOnTable.find(note) != fKeyOnTable.end()) { | |||
for (unsigned int i = 0; i < fKeyOnTable[note].size(); i++) { | |||
fKeyOnTable[note][i]->modifyZone(FAUSTFLOAT(velocity)); | |||
} | |||
} | |||
return 0; | |||
} | |||
void keyOff(double date, int channel, int note, int velocity) | |||
{ | |||
if (fKeyOffTable.find(note) != fKeyOffTable.end()) { | |||
for (unsigned int i = 0; i < fKeyOffTable[note].size(); i++) { | |||
fKeyOffTable[note][i]->modifyZone(FAUSTFLOAT(velocity)); | |||
} | |||
} | |||
} | |||
void ctrlChange(double date, int channel, int ctrl, int value) | |||
{ | |||
if (fCtrlChangeTable.find(ctrl) != fCtrlChangeTable.end()) { | |||
for (unsigned int i = 0; i < fCtrlChangeTable[ctrl].size(); i++) { | |||
fCtrlChangeTable[ctrl][i]->modifyZone(FAUSTFLOAT(value)); | |||
} | |||
} | |||
} | |||
void progChange(double date, int channel, int pgm) | |||
{ | |||
if (fProgChangeTable.find(pgm) != fProgChangeTable.end()) { | |||
for (unsigned int i = 0; i < fProgChangeTable[pgm].size(); i++) { | |||
fProgChangeTable[pgm][i]->modifyZone(FAUSTFLOAT(1)); | |||
} | |||
} | |||
} | |||
void pitchWheel(double date, int channel, int wheel) | |||
{ | |||
for (unsigned int i = 0; i < fPitchWheelTable.size(); i++) { | |||
fPitchWheelTable[i]->modifyZone(FAUSTFLOAT(wheel)); | |||
} | |||
} | |||
void keyPress(double date, int channel, int pitch, int press) | |||
{ | |||
if (fKeyPressTable.find(press) != fKeyPressTable.end()) { | |||
for (unsigned int i = 0; i < fKeyPressTable[press].size(); i++) { | |||
fKeyPressTable[press][i]->modifyZone(FAUSTFLOAT(press)); | |||
} | |||
} | |||
} | |||
void chanPress(double date, int channel, int press) | |||
{ | |||
if (fChanPressTable.find(press) != fChanPressTable.end()) { | |||
for (unsigned int i = 0; i < fChanPressTable[press].size(); i++) { | |||
fChanPressTable[press][i]->modifyZone(FAUSTFLOAT(1)); | |||
} | |||
} | |||
} | |||
void ctrlChange14bits(double date, int channel, int ctrl, int value) {} | |||
// MIDI sync | |||
void start_sync(double date) | |||
{ | |||
for (unsigned int i = 0; i < fStartTable.size(); i++) { | |||
fStartTable[i]->modifyZone(date, FAUSTFLOAT(1)); | |||
} | |||
} | |||
void stop_sync(double date) | |||
{ | |||
for (unsigned int i = 0; i < fStopTable.size(); i++) { | |||
fStopTable[i]->modifyZone(date, FAUSTFLOAT(0)); | |||
} | |||
} | |||
void clock(double date) | |||
{ | |||
for (unsigned int i = 0; i < fClockTable.size(); i++) { | |||
fClockTable[i]->modifyZone(date, FAUSTFLOAT(1)); | |||
} | |||
} | |||
}; | |||
#endif // FAUST_MIDIUI_H |
@@ -0,0 +1,672 @@ | |||
/****************************************************************************** | |||
******************************************************************************* | |||
OPENCV USER INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
#ifndef _OCVUI_H | |||
#define _OCVUI_H | |||
/** | |||
* \file OCVUI.h | |||
* \brief OpenCV user interface | |||
* \author GRAME, Centre National de Création Musicale | |||
* \date 26/01/2015 | |||
* | |||
* This architecture file allows the user to use the OpenCV library in order to perform | |||
* image processing and use the result to control audio parameters. | |||
* | |||
* To use this mode, just add the option -ocv with your faust2jack tool. | |||
* | |||
*/ | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
// OpenCV includes | |||
#include "opencv2/video/tracking.hpp" | |||
#include "opencv2/imgproc/imgproc.hpp" | |||
#include "opencv2/highgui/highgui.hpp" | |||
// Basic includes | |||
#include <iostream> | |||
#include <ctype.h> | |||
// std::thread | |||
#include <pthread.h> | |||
// OpenCV Main Loop Function Prototype | |||
static void* ocvLoop(void*); | |||
//******** OpenCV User Interface CLASS DEFINITION ********// | |||
class OCVUI : public UI | |||
{ | |||
public: | |||
// STRUCTURES | |||
/** | |||
* \struct object | |||
* \brief parameters of an object detected in the image | |||
* | |||
* An object is assimilated to a circle, and characterised by | |||
* its color, its center, its area, and its radius. | |||
* | |||
*/ | |||
struct object | |||
{ | |||
int color; /*!< Object's color */ | |||
float centerX; /*!< Object's center's abscissa */ | |||
float centerY; /*!< Object's center's ordinate */ | |||
float area; /*!< Object's area */ | |||
int radius; /*!< Object's radius */ | |||
}; | |||
/** | |||
* \struct metadata | |||
* \brief metadata for audio parameters | |||
* | |||
* OpenCV metadata specify which object's characteristics you | |||
* want for an audio parameter. | |||
* | |||
*/ | |||
struct metadata | |||
{ | |||
FAUSTFLOAT* zone; /*!< Audio parameter's address */ | |||
int color; /*!< Object's color */ | |||
std::string param; /*!< Object's parameter */ | |||
bool used; /*!< Bool variable */ | |||
}; | |||
// FUNCTIONS | |||
//-- UI Functions Redefinition | |||
// Functions inherited from the UI class | |||
// Constructor | |||
OCVUI() : objects_storage_(0), parameters_storage_(0), height_(0), width_(0){}; | |||
// Destructor | |||
~OCVUI() | |||
{ | |||
exit_ = true; | |||
pthread_join(loop_, NULL); | |||
}; | |||
// -- WIDGETS LAYOUTS | |||
void openTabBox(const char* label){} | |||
void openHorizontalBox(const char* label){} | |||
void openVerticalBox(const char* label){} | |||
void closeBox(){} | |||
// -- ACTIVE WIDGETS | |||
void addButton(const char* label, FAUSTFLOAT* zone){} | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone){} | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, | |||
FAUSTFLOAT max, FAUSTFLOAT step){} | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, | |||
FAUSTFLOAT max, FAUSTFLOAT step){} | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, | |||
FAUSTFLOAT max, FAUSTFLOAT step){} | |||
// -- PASSIVE WIDGETS | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max){} | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max){} | |||
// -- METADATA DECLARATION | |||
/** | |||
* \fn bool parser(std::string string2parse, metadata *pmeta) | |||
* \brief Parsing Function | |||
* | |||
* \param string2parse A string to parse | |||
* \param pmeta Pointer on a metadata structure | |||
* | |||
* This function parses the metadata string, and puts the parameters | |||
* in a metadata structure. | |||
*/ | |||
bool parser(std::string string2parse, metadata *pmeta) | |||
{ | |||
int SPACE = 32; // Parameters separator | |||
std::vector<std::string> parameters(0); | |||
// String analysis | |||
for (int i = 0 ; i < string2parse.size() ; i++) | |||
{ | |||
if (string2parse[i] == SPACE) | |||
{ | |||
std::string oneParameter= string2parse.substr(0, i); | |||
parameters.push_back(oneParameter); | |||
string2parse.erase(string2parse.begin(), string2parse.begin()+i+1); | |||
i = 0; | |||
} | |||
} | |||
std::string lastParameter = string2parse; | |||
parameters.push_back(lastParameter); | |||
// Store Parameters in a Metadata Structure | |||
// Parameters count must be 2 | |||
if (parameters.size() == 2) | |||
{ | |||
/** | |||
* \enum color | |||
* \brief color indexes | |||
* | |||
* Colors are indexed | |||
*/ | |||
if (parameters[0]=="red") /*!< red = 1 */ | |||
{ | |||
pmeta->color = 1; | |||
} | |||
else if (parameters[0]=="yellow") /*!< yellow = 2 */ | |||
{ | |||
pmeta->color = 2; | |||
} | |||
else if (parameters[0]=="green") /*!< green = 3 */ | |||
{ | |||
pmeta->color = 3; | |||
} | |||
else if (parameters[0]=="cyan") /*!< cyan = 4 */ | |||
{ | |||
pmeta->color = 4; | |||
} | |||
else if (parameters[0]=="blue") /*!< blue = 5 */ | |||
{ | |||
pmeta->color = 5; | |||
} | |||
else if (parameters[0]=="magenta") /*!< magenta = 6 */ | |||
{ | |||
pmeta->color = 6; | |||
} | |||
pmeta->param = parameters[1]; | |||
pmeta->used = false; | |||
return true; | |||
} | |||
else | |||
{ | |||
std::cout<<"Wrong count of parameters. Please check if the OpenCV" | |||
<<"metadata is correctly defined"<<std::endl; | |||
return false; | |||
} | |||
} | |||
/** | |||
* \fn void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
* \brief metadata declaration | |||
* | |||
* \param zone audio parameter's address | |||
* \param key metadata key/type (here, it must be ocv) | |||
* \param val metadata value | |||
* | |||
* This function gives the metadata string, which will be analysed. | |||
*/ | |||
void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{ | |||
if (key == "ocv") | |||
{ | |||
metadata newMeta; | |||
bool string_parsed = false; | |||
if (zone != 0) | |||
{ | |||
newMeta.zone = zone; | |||
} | |||
string_parsed = parser(val, &newMeta); | |||
if (string_parsed) | |||
{ | |||
parameters_storage_.push_back(newMeta); | |||
} | |||
} | |||
} | |||
// Image Processing Functions | |||
/** | |||
* \fn void contoursProcess(std::vector<std::vector<cv::Point>> contours, int color) | |||
* \brief Contours processing | |||
* | |||
* \param contours contours of an object | |||
* \param color color of this object | |||
* | |||
* This function approximates contours to rectangles, | |||
* and stores the bigest one as a new object. | |||
*/ | |||
void contoursProcess(std::vector<std::vector<cv::Point> > contours, int color) | |||
{ | |||
int tempArea = 0; | |||
cv::Rect myRect; | |||
for (int j = 0 ; j<contours.size() ; j++) | |||
{ | |||
std::vector<std::vector<cv::Point> > contours_poly( contours.size() ); | |||
std::vector<cv::Rect> boundRect( contours.size() ); | |||
if (contours[j].size() > 40) // Do not take care about small | |||
// contours | |||
{ | |||
approxPolyDP( cv::Mat(contours[j]), contours_poly[j], 3, true );// Approximate contours to | |||
// a polygone | |||
boundRect[j] = cv::boundingRect( cv::Mat(contours_poly[j]) ); // Bound a contour in a | |||
// rectangle | |||
if ((int)boundRect[j].area()>tempArea) | |||
{ | |||
tempArea=(int)boundRect[j].area(); | |||
myRect = boundRect[j]; | |||
} | |||
} | |||
} | |||
if (tempArea != 0) | |||
{ | |||
// Create a new object structure to store the object properties | |||
object newObject; | |||
newObject.color = color; | |||
newObject.centerX = myRect.x+myRect.width/2; | |||
newObject.centerY = myRect.y+myRect.height/2; | |||
newObject.area = 1.5*(float)tempArea/(width_*height_); | |||
newObject.radius= (int)MIN(myRect.width/2, myRect.height/2); | |||
// Put the new object in the objects storage. | |||
objects_storage_.push_back(newObject); | |||
} | |||
} | |||
/** | |||
* \fn void erodeAndDilate(cv::Mat image) | |||
* \brief Morphological Opening (Erosion and Dilatation) | |||
* | |||
* \param image mask produced with the "cv::inRange" function. | |||
* | |||
* This function improves a mask shape | |||
* See OpenCV documentation for more informations : | |||
* http://docs.opencv.org/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html | |||
*/ | |||
void erodeAndDilate(cv::Mat image) | |||
{ | |||
cv::Mat element; | |||
element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3)); | |||
// Erase small alone pixels | |||
// http://docs.opencv.org/modules/imgproc/doc/filtering.html#dilate | |||
for (int i = 0; i<2 ; i++) | |||
{ | |||
cv::erode(image, image, element); | |||
} | |||
// Enlarge blocks of pixels | |||
// http://docs.opencv.org/modules/imgproc/doc/filtering.html#erode | |||
for (int i = 0; i<10 ; i++) | |||
{ | |||
cv::dilate(image, image, element); | |||
} | |||
} | |||
/** | |||
* \fn void drawCircle(object my_object, cv::Mat my_image) | |||
* \brief Draws circles around chosen objects | |||
* | |||
* \param my_object Detected and specified object | |||
* \param my_image image on which to draw | |||
* | |||
* This function draws circles around the objects specified in the metadata | |||
* declaration and detected in the image. | |||
* Note that the circle color depends on the object color. | |||
*/ | |||
void drawCircle(object my_object, cv::Mat my_image) | |||
{ | |||
cv::Scalar bgr_color; | |||
switch (my_object.color) | |||
{ | |||
case 1: // RED | |||
bgr_color = cv::Scalar (0,0,255); | |||
break; | |||
case 2: //YELLOW | |||
bgr_color = cv::Scalar (0, 255, 255); | |||
break; | |||
case 3: // GREEN | |||
bgr_color = cv::Scalar (0, 255, 0); | |||
break; | |||
case 4: // CYAN | |||
bgr_color = cv::Scalar (255, 255, 0); | |||
break; | |||
case 5: // BLUE | |||
bgr_color = cv::Scalar (255,0,0); | |||
break; | |||
case 6: // MAGENTA | |||
bgr_color = cv::Scalar (255, 0, 255); | |||
break; | |||
default: // Add a color ! | |||
break; | |||
} | |||
// draw circle | |||
cv::circle(my_image, cv::Point(my_object.centerX, my_object.centerY), | |||
my_object.radius, bgr_color, 2, 8, 0); | |||
} | |||
/** | |||
* \fn imageProcessing(cv::Mat BGRImage) | |||
* \brief Image Processing function for objects detection | |||
* | |||
* \param BGRImage image in BGR color scale, from camera | |||
* | |||
* This function processes a BGR image. | |||
* It converts it into an HSV image, opens it (erodes and dilates), extracts contours from image | |||
* and extracts objects from contours. The objects are stored and circled. | |||
*/ | |||
void imageProcessing(cv::Mat BGRImage) | |||
{ | |||
height_ = BGRImage.rows; | |||
width_ = BGRImage.cols; | |||
cv::Mat HsvImage; | |||
cv::cvtColor(BGRImage, HsvImage, CV_BGR2HSV); // Convert frame to HSV format | |||
// in order to use "inRange" | |||
// Mask matrices (red, yellow, green, cyan, blue and magenta) | |||
cv::Mat r_mask, y_mask, g_mask, c_mask, b_mask, m_mask; | |||
// Objects contours | |||
std::vector<std::vector<cv::Point> > r_contours, y_contours, g_contours, | |||
c_contours, b_contours, m_contours; | |||
std::vector<cv::Vec4i> hierarchy; | |||
// Get every pixel whose value is between _min and _max | |||
// and put it into a mask | |||
cv::inRange(BGRImage, red_min, red_max, r_mask); | |||
cv::inRange(BGRImage, yellow_min, yellow_max, y_mask); | |||
cv::inRange(BGRImage, green_min, green_max, g_mask); | |||
cv::inRange(BGRImage, cyan_min, cyan_max, c_mask); | |||
cv::inRange(BGRImage, blue_min, blue_max, b_mask); | |||
cv::inRange(BGRImage, magenta_min, magenta_max, m_mask); | |||
// Improve masks shapes | |||
erodeAndDilate(r_mask); | |||
erodeAndDilate(y_mask); | |||
erodeAndDilate(g_mask); | |||
erodeAndDilate(c_mask); | |||
erodeAndDilate(b_mask); | |||
erodeAndDilate(m_mask); | |||
// Get the shapes contours from masks | |||
cv::findContours(r_mask, r_contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) ); | |||
cv::findContours(y_mask, y_contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) ); | |||
cv::findContours(g_mask, g_contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) ); | |||
cv::findContours(c_mask, c_contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) ); | |||
cv::findContours(b_mask, b_contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) ); | |||
cv::findContours(m_mask, m_contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) ); | |||
// Process every contour. Note that color is taken in account. | |||
for (int i = 1; i <= 6; i++) | |||
{ | |||
switch (i) | |||
{ | |||
case 1: // RED | |||
contoursProcess(r_contours, 1); | |||
break; | |||
case 2: // YELLOW | |||
contoursProcess(y_contours, 2); | |||
break; | |||
case 3: // GREEN | |||
contoursProcess(g_contours, 3); | |||
break; | |||
case 4: // CYAN | |||
contoursProcess(c_contours, 4); | |||
break; | |||
case 5: // BLUE | |||
contoursProcess(b_contours, 5); | |||
break; | |||
case 6: // MAGENTA | |||
contoursProcess(m_contours, 6); | |||
break; | |||
default: // You'll have to add a new color... | |||
break; | |||
} | |||
} | |||
// Audio parameters setting | |||
for (int i = 0; i<objects_storage_.size(); i++) | |||
{ | |||
for (int j = 0; j < parameters_storage_.size(); j++) | |||
{ | |||
if (objects_storage_[i].color == parameters_storage_[j].color | |||
&& !parameters_storage_[j].used) | |||
{ | |||
if (parameters_storage_[j].param=="color") | |||
{ | |||
*parameters_storage_[j].zone=(float)objects_storage_[i].color; | |||
} | |||
else if (parameters_storage_[j].param=="x") | |||
{ | |||
*parameters_storage_[j].zone=objects_storage_[i].centerX/width_; | |||
} | |||
else if (parameters_storage_[j].param=="y") | |||
{ | |||
*parameters_storage_[j].zone=1-(objects_storage_[i].centerY/height_); | |||
} | |||
else if (parameters_storage_[j].param=="area") | |||
{ | |||
*parameters_storage_[j].zone=(float)objects_storage_[i].area; | |||
} | |||
parameters_storage_[j].used=true; | |||
drawCircle(objects_storage_[i], BGRImage); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* \fn void empty() | |||
* \brief Empties the object's storage | |||
* | |||
* This function empties the object's storage, and resets the parameters storage | |||
*/ | |||
void empty() | |||
{ | |||
while (objects_storage_.size() > 0) | |||
{ | |||
objects_storage_.pop_back(); | |||
} | |||
for (int l = 0; l<parameters_storage_.size(); l++) | |||
{ | |||
parameters_storage_[l].used=false; | |||
} | |||
} | |||
/** | |||
* \fn bool exit() | |||
* \brief Return the exit member parameter. | |||
*/ | |||
bool exit() | |||
{ | |||
return exit_; | |||
} | |||
/** | |||
* \fn void exitThread() | |||
* \brief Exit from thread | |||
* | |||
* \param This function exits from thread | |||
*/ | |||
void exitThread() | |||
{ | |||
pthread_exit(NULL); | |||
} | |||
/** | |||
* \fn void run() | |||
* \brief Creates and runs the thread | |||
* | |||
* This function creates the image processing thread | |||
*/ | |||
bool run() | |||
{ | |||
exit_ = false; | |||
if (pthread_create(&loop_, NULL, ocvLoop, (void *)this) == 0) { | |||
return true; | |||
} else { | |||
std::cerr <<"Could not create thread. Thread Creation failed." << std::endl; | |||
return false; | |||
} | |||
} | |||
//////////////////////////////////////////// | |||
//// //// | |||
//// MEMBER VARIABLES //// | |||
//// //// | |||
//////////////////////////////////////////// | |||
private: | |||
// HSV min and max values variables | |||
// #1 : RED | |||
static cv::Scalar red_min; | |||
static cv::Scalar red_max; | |||
// #2 : YELLOW | |||
static cv::Scalar yellow_min; | |||
static cv::Scalar yellow_max; | |||
// #3 : GREEN | |||
static cv::Scalar green_min; | |||
static cv::Scalar green_max; | |||
// #4 : CYAN | |||
static cv::Scalar cyan_min; | |||
static cv::Scalar cyan_max; | |||
// #5 : BLUE | |||
static cv::Scalar blue_min; | |||
static cv::Scalar blue_max; | |||
// #6 : MAGENTA | |||
static cv::Scalar magenta_min; | |||
static cv::Scalar magenta_max; | |||
// Objects Storage | |||
// Where all the objects are stored | |||
std::vector<object> objects_storage_; | |||
// Parameters Storage | |||
// Where all the "ocv" metadata parameters are stored | |||
std::vector<metadata> parameters_storage_; | |||
// Matrix height and width | |||
int height_, width_; | |||
// Loop thread; | |||
pthread_t loop_; | |||
// Thread EXIT variable | |||
bool exit_; | |||
}; | |||
// HSV min and max values | |||
// Note that H is between 0 and 180 | |||
// in openCV | |||
// #1 = RED | |||
cv::Scalar OCVUI::red_min = cv::Scalar (0,200,55); | |||
cv::Scalar OCVUI::red_max = cv::Scalar (1,255,255); | |||
// #2 = YELLOW | |||
cv::Scalar OCVUI::yellow_min = cv::Scalar (25, 200, 55); | |||
cv::Scalar OCVUI::yellow_max = cv::Scalar (35, 255, 255); | |||
// #3 = GREEN | |||
cv::Scalar OCVUI::green_min = cv::Scalar (20,155,55); | |||
cv::Scalar OCVUI::green_max = cv::Scalar (50,255,255); | |||
// #4 = CYAN | |||
cv::Scalar OCVUI::cyan_min = cv::Scalar (85,200,55); | |||
cv::Scalar OCVUI::cyan_max = cv::Scalar (95,200,55); | |||
// #5 = BLUE | |||
cv::Scalar OCVUI::blue_min = cv::Scalar (115,155,55); | |||
cv::Scalar OCVUI::blue_max = cv::Scalar (125,255,255); | |||
// #6 = MAGENTA | |||
cv::Scalar OCVUI::magenta_min = cv::Scalar (145,200,55); | |||
cv::Scalar OCVUI::magenta_max = cv::Scalar (155,255,255); | |||
// OpenCV Main Loop Function Implementation | |||
// This function is a loop that gets every frame from a camera | |||
// and calls the image processing functions. | |||
// This is the OCVUI.h main function. | |||
/** | |||
* \fn void* ocvLoop(void* ocv_object) | |||
* \brief Loop function for image processing | |||
*/ | |||
void* ocvLoop(void* ocv_object) | |||
{ | |||
// The camera index allows to select the camera. | |||
// 0 stands for the default camera. | |||
int camIndex=1; | |||
//std::cout<<"camera index ?"<<std::endl; | |||
//std::cin>>camIndex; | |||
cv::Mat frame, hsv; | |||
OCVUI* ocv = (OCVUI*) ocv_object; | |||
cv::VideoCapture cap(camIndex); | |||
std::cout<<"Video Capture from camera n°"<<camIndex<<std::endl; | |||
if (!cap.isOpened()) // check if we succeeded to read frames | |||
// from camera | |||
{ | |||
std::cout<<"Could not open camera n°"<<camIndex<<" !"<<std::endl; | |||
} | |||
cap.set(CV_CAP_PROP_FPS, 60); // Set frames rate | |||
cv::namedWindow("Tracking", 1); // Create a window | |||
while (!ocv->exit()) | |||
{ | |||
cap >> frame; // Get frame from camera | |||
ocv->imageProcessing(frame); // Objects Detection function | |||
/*** Show image ***/ | |||
cv::imshow("Tracking", frame); | |||
ocv->empty(); // Empty the objects and parameters storages | |||
} | |||
ocv->exitThread(); | |||
} | |||
#endif |
@@ -0,0 +1,118 @@ | |||
/* | |||
Faust Project | |||
Copyright (C) 2011 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __OSCControler__ | |||
#define __OSCControler__ | |||
#include <string> | |||
#include "faust/osc/FaustFactory.h" | |||
class GUI; | |||
typedef void (*ErrorCallback)(void*); | |||
namespace oscfaust | |||
{ | |||
class OSCIO; | |||
class OSCSetup; | |||
class OSCRegexp; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief the main Faust OSC Lib API | |||
The OSCControler is essentially a glue between the memory representation (in charge of the FaustFactory), | |||
and the network services (in charge of OSCSetup). | |||
*/ | |||
class OSCControler | |||
{ | |||
int fUDPPort, fUDPOut, fUPDErr; // the udp ports numbers | |||
std::string fDestAddress; // the osc messages destination address, used at initialization only | |||
// to collect the address from the command line | |||
OSCSetup* fOsc; // the network manager (handles the udp sockets) | |||
OSCIO* fIO; // hack for OSC IO support (actually only relayed to the factory) | |||
FaustFactory * fFactory; // a factory to build the memory represetnatin | |||
bool fInit; | |||
public: | |||
/* | |||
base udp port is chosen in an unassigned range from IANA PORT NUMBERS (last updated 2011-01-24) | |||
see at http://www.iana.org/assignments/port-numbers | |||
5507-5552 Unassigned | |||
*/ | |||
enum { kUDPBasePort = 5510}; | |||
OSCControler(int argc, char *argv[], GUI* ui, OSCIO* io = 0, ErrorCallback errCallback = NULL, void* arg = NULL, bool init = true); | |||
virtual ~OSCControler(); | |||
//-------------------------------------------------------------------------- | |||
// addnode, opengroup and closegroup are simply relayed to the factory | |||
//-------------------------------------------------------------------------- | |||
// Add a node in the current group (top of the group stack) | |||
template <typename T> void addnode(const char* label, T* zone, T init, T min, T max, bool input = true) | |||
{ fFactory->addnode(label, zone, init, min, max, fInit, input); } | |||
//-------------------------------------------------------------------------- | |||
// This method is used for alias messages. The arguments imin and imax allow | |||
// to map incomming values from the alias input range to the actual range | |||
template <typename T> void addAlias(const std::string& fullpath, T* zone, T imin, T imax, T init, T min, T max, const char* label) | |||
{ fFactory->addAlias(fullpath, zone, imin, imax, init, min, max, label); } | |||
void opengroup(const char* label) { fFactory->opengroup(label); } | |||
void closegroup() { fFactory->closegroup(); } | |||
//-------------------------------------------------------------------------- | |||
void run(); // starts the network services | |||
void stop(); // stop the network services | |||
int getUDPPort() const { return fUDPPort; } | |||
int getUDPOut() const { return fUDPOut; } | |||
int getUDPErr() const { return fUPDErr; } | |||
const char* getDestAddress() const { return fDestAddress.c_str(); } | |||
const char* getRootName() const; // probably useless, introduced for UI extension experiments | |||
// By default, an osc interface emits all parameters. You can filter specific params dynamically. | |||
static std::vector<OSCRegexp*> fFilteredPaths; // filtered paths will not be emitted | |||
static void addFilteredPath(std::string path); | |||
static bool isPathFiltered(std::string path); | |||
static void resetFilteredPaths(); | |||
static float version(); // the Faust OSC library version number | |||
static const char* versionstr(); // the Faust OSC library version number as a string | |||
static int gXmit; // a static variable to control the transmission of values | |||
// i.e. the use of the interface as a controler | |||
}; | |||
#define kNoXmit 0 | |||
#define kAll 1 | |||
#define kAlias 2 | |||
} | |||
#endif |
@@ -0,0 +1,179 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
/* | |||
Copyright (C) 2011 Grame - Lyon | |||
All rights reserved. | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted. | |||
*/ | |||
#ifndef __OSCUI__ | |||
#define __OSCUI__ | |||
#include "faust/gui/OSCControler.h" | |||
#include "faust/gui/GUI.h" | |||
#include <vector> | |||
#ifdef _WIN32 | |||
#define strcasecmp _stricmp | |||
#endif | |||
/****************************************************************************** | |||
******************************************************************************* | |||
OSC (Open Sound Control) USER INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
/* | |||
Note about the OSC addresses and the Faust UI names: | |||
---------------------------------------------------- | |||
There are potential conflicts between the Faust UI objects naming scheme and | |||
the OSC address space. An OSC symbolic names is an ASCII string consisting of | |||
printable characters other than the following: | |||
space | |||
# number sign | |||
* asterisk | |||
, comma | |||
/ forward | |||
? question mark | |||
[ open bracket | |||
] close bracket | |||
{ open curly brace | |||
} close curly brace | |||
a simple solution to address the problem consists in replacing | |||
space or tabulation with '_' (underscore) | |||
all the other osc excluded characters with '-' (hyphen) | |||
This solution is implemented in the proposed OSC UI; | |||
*/ | |||
class OSCUI : public GUI | |||
{ | |||
oscfaust::OSCControler* fCtrl; | |||
std::vector<const char*> fAlias; | |||
const char* tr(const char* label) const | |||
{ | |||
static char buffer[1024]; | |||
char * ptr = buffer; int n=1; | |||
while (*label && (n++ < 1024)) { | |||
switch (*label) { | |||
case ' ': case ' ': | |||
*ptr++ = '_'; | |||
break; | |||
case '#': case '*': case ',': case '/': case '?': | |||
case '[': case ']': case '{': case '}': case '(': case ')': | |||
*ptr++ = '_'; | |||
break; | |||
default: | |||
*ptr++ = *label; | |||
} | |||
label++; | |||
} | |||
*ptr = 0; | |||
return buffer; | |||
} | |||
// add all accumulated alias | |||
void addalias(FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, const char* label) | |||
{ | |||
for (unsigned int i = 0; i < fAlias.size(); i++) { | |||
fCtrl->addAlias(fAlias[i], zone, FAUSTFLOAT(0), FAUSTFLOAT(1), init, min, max, label); | |||
} | |||
fAlias.clear(); | |||
} | |||
public: | |||
OSCUI(const char* /*applicationname*/, int argc, char *argv[], oscfaust::OSCIO* io = 0, ErrorCallback errCallback = NULL, void* arg = NULL, bool init = true) : GUI() | |||
{ | |||
fCtrl = new oscfaust::OSCControler(argc, argv, this, io, errCallback, arg, init); | |||
// fCtrl->opengroup(applicationname); | |||
} | |||
virtual ~OSCUI() { delete fCtrl; } | |||
// -- widget's layouts | |||
virtual void openTabBox(const char* label) { fCtrl->opengroup(tr(label)); } | |||
virtual void openHorizontalBox(const char* label) { fCtrl->opengroup(tr(label)); } | |||
virtual void openVerticalBox(const char* label) { fCtrl->opengroup(tr(label)); } | |||
virtual void closeBox() { fCtrl->closegroup(); } | |||
// -- active widgets | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) { const char* l = tr(label); addalias(zone, 0, 0, 1, l); fCtrl->addnode(l, zone, FAUSTFLOAT(0), FAUSTFLOAT(0), FAUSTFLOAT(1)); } | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) { const char* l = tr(label); addalias(zone, 0, 0, 1, l); fCtrl->addnode(l, zone, FAUSTFLOAT(0), FAUSTFLOAT(0), FAUSTFLOAT(1)); } | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT /*step*/) | |||
{ const char* l = tr(label); addalias(zone, init, min, max, l); fCtrl->addnode(l, zone, init, min, max); } | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT /*step*/) | |||
{ const char* l = tr(label); addalias(zone, init, min, max, l); fCtrl->addnode(l, zone, init, min, max); } | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT /*step*/) | |||
{ const char* l = tr(label); addalias(zone, init, min, max, l); fCtrl->addnode(l, zone, init, min, max); } | |||
// -- passive widgets | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
const char* l = tr(label); addalias(zone, 0, min, max, l); fCtrl->addnode(l, zone, FAUSTFLOAT(0), min, max, false); | |||
} | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
const char* l = tr(label); addalias(zone, 0, min, max, l); fCtrl->addnode(l, zone, FAUSTFLOAT(0), min, max, false); | |||
} | |||
// -- metadata declarations | |||
virtual void declare(FAUSTFLOAT* , const char* key , const char* alias) | |||
{ | |||
if (strcasecmp(key,"OSC") == 0) fAlias.push_back(alias); | |||
} | |||
virtual void show() {} | |||
bool run() | |||
{ | |||
fCtrl->run(); | |||
return true; | |||
} | |||
void stop() | |||
{ | |||
fCtrl->stop(); | |||
} | |||
const char* getRootName() { return fCtrl->getRootName(); } | |||
int getUDPPort() { return fCtrl->getUDPPort(); } | |||
int getUDPOut() { return fCtrl->getUDPOut(); } | |||
int getUDPErr() { return fCtrl->getUDPErr(); } | |||
const char* getDestAddress() { return fCtrl->getDestAddress(); } | |||
}; | |||
#endif // __OSCUI__ |
@@ -0,0 +1,65 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef FAUST_PATHBUILDER_H | |||
#define FAUST_PATHBUILDER_H | |||
#include <vector> | |||
#include <string> | |||
#include <algorithm> | |||
/******************************************************************************* | |||
* PathBuilder : Faust User Interface | |||
* Helper class to build complete hierarchical path for UI items. | |||
******************************************************************************/ | |||
class PathBuilder | |||
{ | |||
protected: | |||
std::vector<std::string> fControlsLevel; | |||
public: | |||
PathBuilder() {} | |||
virtual ~PathBuilder() {} | |||
std::string buildPath(const std::string& label) | |||
{ | |||
std::string res = "/"; | |||
for (size_t i = 0; i < fControlsLevel.size(); i++) { | |||
res += fControlsLevel[i]; | |||
res += "/"; | |||
} | |||
res += label; | |||
replace(res.begin(), res.end(), ' ', '_'); | |||
return res; | |||
} | |||
}; | |||
#endif // FAUST_PATHBUILDER_H |
@@ -0,0 +1,119 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef FAUST_PRINTUI_H | |||
#define FAUST_PRINTUI_H | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
#include "faust/gui/UI.h" | |||
#include "faust/gui/PathBuilder.h" | |||
#include <vector> | |||
#include <string> | |||
/******************************************************************************* | |||
* PrintUI : Faust User Interface | |||
* This class print arguments given to calls to UI methods and build complete path for labels. | |||
******************************************************************************/ | |||
class PrintUI : public PathBuilder, public UI | |||
{ | |||
public: | |||
PrintUI() {} | |||
virtual ~PrintUI() {} | |||
// -- widget's layouts | |||
virtual void openTabBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
std::cout << "openTabBox label : " << label << std::endl; | |||
} | |||
virtual void openHorizontalBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
std::cout << "openHorizontalBox label : " << label << std::endl; | |||
} | |||
virtual void openVerticalBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
std::cout << "openVerticalBox label : " << label << std::endl; | |||
} | |||
virtual void closeBox() | |||
{ | |||
fControlsLevel.pop_back(); | |||
std::cout << "closeBox" << std::endl; | |||
} | |||
// -- active widgets | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
std::cout << "addButton label : " << buildPath(label) << std::endl; | |||
} | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
std::cout << "addCheckButton label : " << buildPath(label) << std::endl; | |||
} | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
std::cout << "addVerticalSlider label : " << buildPath(label) << " init : " << init << " min : " << min << " max : " << max << " step : " << step << std::endl; | |||
} | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
std::cout << "addHorizontalSlider label : " << buildPath(label) << " init : " << init << " min : " << min << " max : " << max << " step : " << step << std::endl; | |||
} | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
std::cout << "addNumEntry label : " << buildPath(label) << " init : " << init << " min : " << min << " max : " << max << " step : " << step << std::endl; | |||
} | |||
// -- passive widgets | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
std::cout << "addHorizontalBargraph label : " << buildPath(label) << " min : " << min << " max : " << max << std::endl; | |||
} | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
std::cout << "addVerticalBargraph label : " << buildPath(label) << " min : " << min << " max : " << max << std::endl; | |||
} | |||
// -- metadata declarations | |||
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{ | |||
std::cout << "declare key : " << key << " val : " << val << std::endl; | |||
} | |||
}; | |||
#endif // FAUST_PRINTUI_H |
@@ -0,0 +1,471 @@ | |||
/********************************************** | |||
* ROS Callbacks Interface | |||
* | |||
* This interface allows the user to use ROS | |||
* metadata | |||
* It handles ROS metadata, and writes the | |||
* callbacks directly in the .cpp file. | |||
* | |||
**********************************************/ | |||
#ifndef FAUST_RosCI_H | |||
#define FAUST_RosCI_H | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
#include "faust/gui/UI.h" | |||
#include <algorithm> | |||
#include <vector> | |||
#include <fstream> | |||
#include <iostream> | |||
#include <string> | |||
#include <sstream> | |||
class RosCI : public UI | |||
{ | |||
public: | |||
RosCI(): count_(0), use_slider_values_(false), meta_(false) | |||
{}; | |||
virtual ~RosCI() {} | |||
// String processing function | |||
std::string strProcess(std::string label) | |||
{ | |||
int count = label.size(); | |||
bool ok = false; | |||
int FORWARD_SLASH = 47; | |||
int TILDE = 126; | |||
int UNDERSCORE = 95; | |||
int SPACE = 32; | |||
int LEFT_BRACKET = 40; | |||
int RIGHT_BRACKET = 41; | |||
do | |||
{ | |||
if ((label[0]<65) // before "A" in ASCII | |||
|| (label[0] <= 96 && label[0] >= 91) // After "Z" and before "a" in ASCII | |||
|| (label[0] > 122) // After "z" in ASCII | |||
&& (label[0] != FORWARD_SLASH) // not "/" | |||
&& (label[0] != TILDE) // not "~" | |||
) | |||
{ | |||
label.erase(0,1); | |||
count = label.size(); | |||
} | |||
else if(count < 1) | |||
{ | |||
label = "/topic"; | |||
count = label.size(); | |||
ok = true; | |||
} | |||
else | |||
{ | |||
ok = true; | |||
} | |||
} | |||
while (!ok); | |||
for (int i = 0; i < count; i++) | |||
{ | |||
if ((label[i] <= 90 && label[i] >= 65) // A-Z | |||
|| (label[i] <= 122 && label[i] >= 97) // a-z | |||
|| (label[i] <= 57 && label[i] >= 47) // 0-9 | |||
|| label[i] == UNDERSCORE | |||
) | |||
{ | |||
} | |||
else if (label[i] == SPACE) | |||
{ | |||
if (label[i-1] == UNDERSCORE) | |||
{ | |||
label.erase(i,1); | |||
i = i-1; | |||
count = label.size(); | |||
} | |||
else | |||
label[i]='_'; | |||
} | |||
else if(label[i]== LEFT_BRACKET) // in case of '(' | |||
{ | |||
if (label[i-1] == 95) | |||
{ | |||
label.erase(i,1); | |||
i = i-1; | |||
count = label.size(); | |||
} | |||
else | |||
label[i]='_'; | |||
} | |||
else if (label[i] == RIGHT_BRACKET) // in case of ')' | |||
{ | |||
label.erase(i,1); | |||
i = i-1; | |||
count = label.size(); | |||
} | |||
else | |||
{ | |||
label.erase(i, 1); | |||
i = i-1; | |||
count = label.size(); | |||
} | |||
} | |||
return (label); | |||
} | |||
// -- widget's layouts | |||
void openTabBox(const char* label) | |||
{} | |||
void openHorizontalBox(const char* label) | |||
{} | |||
void openVerticalBox(const char* label) | |||
{} | |||
void closeBox() | |||
{} | |||
// -- active widgets | |||
void addButton(const char* label, FAUSTFLOAT* zone) | |||
{} | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{} | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, | |||
FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
*zone = init; | |||
if (meta_) | |||
{ | |||
if (use_slider_values_) | |||
{ | |||
callbacks_parameters_[count_-1].min_value = min; | |||
callbacks_parameters_[count_-1].max_value = max; | |||
use_slider_values_ = false; | |||
} | |||
callbacks_parameters_[count_-1].slider_min = min; | |||
callbacks_parameters_[count_-1].slider_max = max; | |||
meta_ = false; | |||
} | |||
} | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, | |||
FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
*zone = init; | |||
if (meta_) | |||
{ | |||
if (use_slider_values_) | |||
{ | |||
callbacks_parameters_[count_-1].min_value = min; | |||
callbacks_parameters_[count_-1].max_value = max; | |||
use_slider_values_ = false; | |||
} | |||
callbacks_parameters_[count_-1].slider_min = min; | |||
callbacks_parameters_[count_-1].slider_max = max; | |||
meta_ = false; | |||
} | |||
} | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, | |||
FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
*zone=init; | |||
if (meta_) | |||
{ | |||
if (use_slider_values_) | |||
{ | |||
callbacks_parameters_[count_-1].min_value = min; | |||
callbacks_parameters_[count_-1].max_value = max; | |||
use_slider_values_ = false; | |||
} | |||
callbacks_parameters_[count_-1].slider_min = min; | |||
callbacks_parameters_[count_-1].slider_max = max; | |||
meta_ = false; | |||
} | |||
} | |||
// -- passive widgets | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{} | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{} | |||
// -- metadata declarations | |||
// Structure containing a callback parameters | |||
struct CallbackParams | |||
{ | |||
std::string topic_name; | |||
std::string msg_type; | |||
std::string msg_name; | |||
std::string field_name; | |||
FAUSTFLOAT min_value; | |||
FAUSTFLOAT max_value; | |||
FAUSTFLOAT slider_min; | |||
FAUSTFLOAT slider_max; | |||
}; | |||
// Callback writing the callbacks filekeyboard's arrows | |||
// num is the number of ROS declared metadata | |||
// param_vector is a callbacks parameters structure container | |||
// name is the application name | |||
void callbacksWriter(int num, std::vector<RosCI::CallbackParams> param_vector, std::string name) | |||
{ | |||
// Get file name | |||
name = name + ".cpp"; | |||
const char* file_name = name.c_str(); | |||
std::fstream file (file_name); | |||
if (!file.is_open()) | |||
{ | |||
std::cout<<"unable to open"<<file_name<<std::endl; | |||
return; | |||
} | |||
std::string line, test_line; | |||
bool search_RosUI = true; | |||
std::streampos begin = 0; | |||
std::streampos end=0; | |||
int block_size; | |||
char * memblock; | |||
test_line = "class RosUI : public UI"; // This is the line we look for | |||
// in the file so that we can | |||
// write callbacks before this line | |||
while( search_RosUI ) | |||
{ | |||
std::getline(file,line); | |||
if(line == test_line) | |||
{ | |||
search_RosUI=false; | |||
} | |||
else | |||
{ | |||
// Get the searched line position | |||
begin += (std::streampos)line.size() +(std::streampos)1; | |||
} | |||
} | |||
// Get the end of file position | |||
file.seekp(0,std::ios::end); | |||
end = file.tellp(); | |||
block_size = end-begin; | |||
memblock = new char[block_size]; | |||
// puts the end of the file in a memory block | |||
// in order to overwrite without deleting information | |||
file.seekp(begin, std::ios::beg); | |||
file.read(memblock, block_size); | |||
file.seekp(begin,std::ios::beg); | |||
file <<"/*****************************"<<std::endl | |||
<<"* Code generated automatically"<<std::endl | |||
<<"* "<<std::endl | |||
<<"* See the RosCI.h file"<<std::endl | |||
<<"* \tCallbacksWriter function"<<std::endl | |||
<<"* \tfor more informations"<<std::endl | |||
<<"* "<<std::endl | |||
<<"*****************************/\n"<<std::endl; | |||
// Include messages files if they are different | |||
bool include = true; | |||
for (int i = 0; i < num ; i++) | |||
{ | |||
RosCI::CallbackParams parameters = param_vector[i]; | |||
if (i != 0) | |||
{ | |||
for (int j = 0; j < i; j++) | |||
{ | |||
if (parameters.msg_type == param_vector[j].msg_type) | |||
{ | |||
if (parameters.msg_name == param_vector[j].msg_name) | |||
{ | |||
include = false; | |||
} | |||
} | |||
} | |||
} | |||
if (include) | |||
{ | |||
file << "#include \""<< parameters.msg_type<<"/" | |||
<< parameters.msg_name<<".h\""<<std::endl; | |||
} | |||
} | |||
// New class | |||
file << "class RosCallbacks"<< std::endl | |||
<< "{" << std::endl | |||
<< "\tpublic : \n" << std::endl | |||
<< "\tRosCallbacks(ros::NodeHandle n) : nh_(n)"<<std::endl | |||
<< "\t{};\n"<<std::endl; | |||
// Ranging Function | |||
file << "\tfloat rangeAndConvert(FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT slider_min, "<< std::endl | |||
<< "\t\tFAUSTFLOAT slider_max, float value)" << std::endl | |||
<< "\t{" << std::endl | |||
<< "\t\tif (value < min)" << std::endl | |||
<< "\t\t{" << std::endl | |||
<< "\t\t\tvalue = min;" << std::endl | |||
<< "\t\t}" << std::endl | |||
<< "\t\telse if (value > max)" << std::endl | |||
<< "\t\t{" << std::endl | |||
<< "\t\t\tvalue = max ;" << std::endl | |||
<< "\t\t}" << std::endl | |||
<< "\t\tfloat a = (slider_max - slider_min)/(max-min);" << std::endl | |||
<< "\t\tfloat b = (slider_max + slider_min - a*(max+min))/2;" << std::endl | |||
<< "\t\tvalue = a*value + b;\n" << std::endl | |||
<< "\t\treturn value;" << std::endl | |||
<< "\t}" << std::endl; | |||
// ROS specific callbacks | |||
for (int i = 0; i < num; i++) | |||
{ | |||
RosCI::CallbackParams parameters = param_vector[i]; | |||
file << "\tvoid callback"<<i<<"(const "<< parameters.msg_type<<"::" | |||
<< parameters.msg_name<<"ConstPtr& msg, FAUSTFLOAT* zone)"<<std::endl | |||
<< "\t{"<<std::endl | |||
<< "\t\t FAUSTFLOAT min"<<i<<" = "<<parameters.min_value<<";"<<std::endl | |||
<< "\t\t FAUSTFLOAT max"<<i<<" = "<<parameters.max_value<<";"<<std::endl | |||
<< "\t\t FAUSTFLOAT smin"<<i<<" = "<<parameters.slider_min<<";"<<std::endl | |||
<< "\t\t FAUSTFLOAT smax"<<i<<" = "<<parameters.slider_max<<";\n"<<std::endl | |||
<< "\t\t *zone = rangeAndConvert(min"<<i<<", max"<<i<<", smin"<<i<<", smax"<<i | |||
<< ", (float) msg->"<<parameters.field_name<<");"<<std::endl | |||
<< "\t}\n"<<std::endl; | |||
} | |||
// RosCallbacks class main function : | |||
// When called, it subscribes to all the predefined callbacks | |||
file << "\n\tvoid subscribe(std::vector<FAUSTFLOAT*> zones)\n"<<std::endl | |||
<< "\t{" <<std::endl; | |||
// Declaring subscribers and subscribing | |||
for (int i = 0; i < num; i++) | |||
{ | |||
RosCI::CallbackParams parameters = param_vector[i]; | |||
file << "\t\tros::Subscriber* my_sub"<<i<<" = new ros::Subscriber();"<<std::endl | |||
<< "\t\t*my_sub"<<i<<" = nh_.subscribe<"<<parameters.msg_type<<"::"<<parameters.msg_name | |||
<< ">(\"" <<parameters.topic_name | |||
<< "\", 1,"<<std::endl | |||
<< "\t\t\tboost::bind(&RosCallbacks::callback"<<i | |||
<< ", this, _1, zones["<<i<<"]));\n"<<std::endl; | |||
} | |||
file << "\t}\n"<<std::endl; | |||
// RosCallbacks class private parameter | |||
file << "\tprivate :\n"<<std::endl | |||
<< "\tros::NodeHandle nh_;"<<std::endl; | |||
file << "};\n" << std::endl; | |||
file << memblock; | |||
file.close(); | |||
} | |||
// String parsing function, which detects every callback parameter | |||
// Separators must be spaces, and there must be 4 or 6 arguments | |||
void stringParser(std::string string2parse) | |||
{ | |||
int SPACE = 32; | |||
for (int i = 0; i < string2parse.size(); i++) | |||
{ | |||
if (string2parse[i] == SPACE) | |||
{ | |||
std::string param= string2parse.substr(0,i); | |||
topic_params_.push_back(param); | |||
string2parse.erase(string2parse.begin(), string2parse.begin()+i+1); | |||
i = -1; | |||
} | |||
} | |||
topic_params_.push_back(string2parse); | |||
} | |||
// Function declaring metadata | |||
void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{ | |||
if (key == "ros") // We do not care if key is not "ros" here | |||
{ | |||
stringParser(val); // Parsing the string corresponding to a callback parameters | |||
CallbackParams params; | |||
if (topic_params_.size() == 4 | |||
|| | |||
topic_params_.size() == 6) | |||
{ | |||
// Storing the parameters in a structure... | |||
params.topic_name=strProcess(topic_params_[0]); | |||
params.msg_type=topic_params_[1]; | |||
params.msg_name=topic_params_[2]; | |||
params.field_name=topic_params_[3]; | |||
if (topic_params_.size() == 6) | |||
{ | |||
std::stringstream smin, smax; | |||
smin.str(topic_params_[4]); | |||
smin >> params.min_value; | |||
smax.str(topic_params_[5]); | |||
smax >> params.max_value; | |||
} | |||
else | |||
{ | |||
use_slider_values_ = true; | |||
} | |||
// ... and the structure in a vector | |||
callbacks_parameters_.push_back(params); | |||
count_++; | |||
meta_ = true; | |||
} | |||
else | |||
{ | |||
std::cout<<"Wrong number of parameters in ros metadata declaration !"<<std::endl; | |||
std::cout<<"It should look like : [ros:/my/topic/name msg_type msg_name" | |||
<<" field_name]"<<std::endl; | |||
std::cout<<"Example : [ros:/topic/level std_msgs Float32 data]"<<std::endl; | |||
} | |||
do | |||
{ | |||
topic_params_.pop_back(); | |||
} | |||
while ( !topic_params_.empty()); | |||
} | |||
} | |||
// Function returning the number of metadata declared, | |||
//which means the number of callbacks to call | |||
int getParamsCount() | |||
{ | |||
return count_; | |||
} | |||
// Function returning a vector containing ROS Callbacks parameters structures | |||
std::vector<CallbackParams> getCallbacksParameters() | |||
{ | |||
return callbacks_parameters_; | |||
} | |||
private: | |||
int count_; | |||
bool use_slider_values_; | |||
bool meta_; | |||
std::vector<std::string> topic_params_; | |||
std::vector<CallbackParams> callbacks_parameters_; | |||
}; | |||
#endif |
@@ -0,0 +1,463 @@ | |||
/********************************************** | |||
* ROS User Interface | |||
* | |||
* This interface creates default callbacks | |||
* with default messages types | |||
* It also returns parameters for specific ROS | |||
* callbacks, defined in the RosCallbacks class | |||
* thanks to the RosCI.h and ros-callbacks.cpp | |||
* architecture files | |||
**********************************************/ | |||
#ifndef FAUST_RosUI_H | |||
#define FAUST_RosUI_H | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
#include "faust/gui/UI.h" | |||
#include "ros/ros.h" | |||
#include "std_msgs/Bool.h" | |||
#include "std_msgs/Float32.h" | |||
#include <algorithm> | |||
#include <vector> | |||
class RosUI : public UI | |||
{ | |||
public: | |||
RosUI(ros::NodeHandle nh, std::string nspace) : nh_(nh), queue_(10), count_(0), meta_(false), box_names_(0), zones_(0) | |||
{ | |||
// Get the namespace's name for default topics | |||
box_names_.push_back(nspace); | |||
}; | |||
virtual ~RosUI() {} | |||
// String processing function for topics names | |||
std::string strProcess(std::string label) | |||
{ | |||
int count = label.size(); | |||
bool ok = false; | |||
int FORWARD_SLASH = 47; | |||
int TILDE = 126; | |||
int UNDERSCORE = 95; | |||
int SPACE = 32; | |||
int LEFT_BRACKET = 40; | |||
int RIGHT_BRACKET = 41; | |||
do | |||
{ | |||
if ((label[0] < 65) // before "A" in ASCII | |||
|| (label[0] <= 96 && label[0] >= 91) // After "Z" and before "a" in ASCII | |||
|| (label[0] > 122) // After "z" in ASCII | |||
&& (label[0] != FORWARD_SLASH) // not "/" | |||
&& (label[0] != TILDE) // not "~" | |||
) | |||
{ | |||
label.erase(0,1); | |||
count = label.size(); | |||
} | |||
else if(count <= 1) | |||
{ | |||
label = "/topic"; | |||
count = label.size(); | |||
ok=true; | |||
} | |||
else | |||
{ | |||
ok=true; | |||
} | |||
} | |||
while (!ok); | |||
for (int i=0; i < count; i++) | |||
{ | |||
if ((label[i] <= 90 && label[i] >= 65) // A-Z | |||
|| (label[i] <= 122 && label[i] >= 97) // a-z | |||
|| (label[i] <= 57 && label[i] >= 47) // 0-9 | |||
|| label[i] == UNDERSCORE | |||
) | |||
{ | |||
} | |||
else if (label[i] == SPACE) | |||
{ | |||
if(label[i-1] == UNDERSCORE) | |||
{ | |||
label.erase(i,1); | |||
i=i-1; | |||
count = label.size(); | |||
} | |||
else | |||
label[i] = '_'; | |||
} | |||
else if(label[i] == LEFT_BRACKET) // in case of '(' | |||
{ | |||
if(label[i-1] == 95) | |||
{ | |||
label.erase(i,1); | |||
i=i-1; | |||
count = label.size(); | |||
} | |||
else | |||
label[i] = '_'; | |||
} | |||
else if (label[i] == RIGHT_BRACKET) // in case of ')' | |||
{ | |||
label.erase(i,1); | |||
i=i-1; | |||
count = label.size(); | |||
} | |||
else | |||
{ | |||
label.erase(i, 1); | |||
i=i-1; | |||
count = label.size(); | |||
} | |||
} | |||
return (label); | |||
} | |||
// -- default callbacks | |||
// Default Callbacks : | |||
// Buttons widgets use the std_msgs/Bool message type | |||
// Sliders and Numerical entries use the std_msgs/Float32 message type | |||
void buttonCallback(const std_msgs::BoolConstPtr& msg, FAUSTFLOAT* zone) | |||
{ | |||
*zone = msg->data; | |||
} | |||
void cButtonCallback(const std_msgs::BoolConstPtr& msg, FAUSTFLOAT* zone) | |||
{ | |||
*zone = msg->data; | |||
} | |||
void sliderCallback(const std_msgs::Float32ConstPtr& msg, FAUSTFLOAT* zone) | |||
{ | |||
*zone = msg->data; | |||
} | |||
void numEntryCallback(const std_msgs::Float32ConstPtr& msg, FAUSTFLOAT* zone) | |||
{ | |||
*zone = msg->data; | |||
} | |||
// -- widget's layouts | |||
// Boxes names are stored in a vector so that the default topics names | |||
// fit to the graphic interface | |||
void openTabBox(const char* label) | |||
{ | |||
std::string L = (std::string)label; | |||
if (L == "0x00") // no box name | |||
{ | |||
L = ""; | |||
} | |||
box_names_.push_back(L); | |||
} | |||
void openHorizontalBox(const char* label) | |||
{ | |||
std::string L = (std::string)label; | |||
if (L == "0x00") // no box name | |||
{ | |||
L = ""; | |||
} | |||
box_names_.push_back(L); | |||
} | |||
void openVerticalBox(const char* label) | |||
{ | |||
std::string L = (std::string)label; | |||
if (L == "0x00") // no box name | |||
{ | |||
L = ""; | |||
} | |||
box_names_.push_back(L); | |||
} | |||
void closeBox() | |||
{ | |||
box_names_.pop_back(); | |||
} | |||
// -- active widgets | |||
// Adding a widget is translated into subscribing to a topic | |||
// For each widget, we use default messages types | |||
// Buttons : std_msgs/Bool | |||
// Sliders and Numerical Entries : std_msgs/Float32 | |||
void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
// Gets the button name and processes it to fit to ROS naming conventions | |||
std::string str = (std::string)label; | |||
std::string my_string = strProcess(str); | |||
// Builds the topic name from boxes and button names | |||
if (! box_names_.empty()) | |||
{ | |||
std::string my_name = ""; | |||
for (int i = 0; i<box_names_.size(); i++) | |||
{ | |||
if (box_names_[i] != "") | |||
{ | |||
my_name += "/" + strProcess(box_names_[i]); | |||
} | |||
else | |||
{ | |||
box_names_.erase(box_names_.begin()+i); | |||
} | |||
} | |||
my_string = my_name + "/" + my_string; | |||
} | |||
else | |||
{ | |||
ROS_ERROR("RosUI.h - function addButton : No box name to use ! "); | |||
ROS_INFO("Button's callback will not be subscribed"); | |||
return; | |||
} | |||
// Subscription to buttons callback | |||
ros::Subscriber* button_sub = new ros::Subscriber(); | |||
*button_sub = nh_.subscribe<std_msgs::Bool>(my_string, queue_, | |||
boost::bind(&RosUI::buttonCallback, this, _1, zone)); | |||
count_++; | |||
} | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
// Gets the check button name and processes it to fit to ROS naming conventions | |||
std::string str = (std::string)label; | |||
std::string my_string = strProcess(str); | |||
// Builds the topic name from boxes and check button names | |||
if (! box_names_.empty()) | |||
{ | |||
std::string my_name = ""; | |||
for (int i = 0; i<box_names_.size(); i++) | |||
{ | |||
if (box_names_[i] != "") | |||
{ | |||
my_name += "/" + strProcess(box_names_[i]); | |||
} | |||
else | |||
{ | |||
box_names_.erase(box_names_.begin()+i); | |||
} | |||
} | |||
my_string = my_name + "/" + my_string; | |||
} | |||
else | |||
{ | |||
ROS_ERROR("RosUI.h - function addCheckButton : No box name to use ! "); | |||
ROS_INFO("Check button's callback will not be subscribed"); | |||
return; | |||
} | |||
// Subscription to check buttons callback | |||
ros::Subscriber* c_button_sub = new ros::Subscriber(); | |||
*c_button_sub = nh_.subscribe<std_msgs::Bool>(my_string, queue_, | |||
boost::bind(&RosUI::cButtonCallback, this, _1, zone)); | |||
count_++; | |||
} | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, | |||
FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
// Gets the vertical slider name and processes it to fit to ROS naming conventions | |||
std::string str = (std::string)label; | |||
std::string my_string = strProcess(str); | |||
// Builds the topic name from boxes and vertical slider names | |||
if (! box_names_.empty()) | |||
{ | |||
std::string my_name = ""; | |||
for (int i = 0; i<box_names_.size(); i++) | |||
{ | |||
if (box_names_[i] != "") | |||
{ | |||
my_name += "/" + strProcess(box_names_[i]); | |||
} | |||
else | |||
{ | |||
box_names_.erase(box_names_.begin()+i); | |||
} | |||
} | |||
my_string = my_name + "/" + my_string; | |||
} | |||
else | |||
{ | |||
ROS_ERROR("RosUI.h - function addVerticalSlider : No box name to use ! "); | |||
ROS_INFO("Vertical slider's callback will not be subscribed"); | |||
return; | |||
} | |||
// Subscription to sliders callback | |||
ros::Subscriber* v_slider = new ros::Subscriber(); | |||
*v_slider = nh_.subscribe<std_msgs::Float32>(my_string, queue_, | |||
boost::bind(&RosUI::sliderCallback, this, _1, zone)); | |||
count_++; | |||
} | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, | |||
FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
// Gets the horizontal slider name and processes it to fit to ROS naming conventions | |||
std::string str = (std::string)label; | |||
std::string my_string = strProcess(str); | |||
// Builds the topic name from boxes and horizontal slider names | |||
if (!box_names_.empty()) | |||
{ | |||
std::string my_name = ""; | |||
for (int i = 0 ; i<box_names_.size() ; i++) | |||
{ | |||
if (box_names_[i] != "") | |||
{ | |||
my_name += "/" + strProcess(box_names_[i]); | |||
} | |||
else | |||
{ | |||
box_names_.erase(box_names_.begin()+i); | |||
} | |||
} | |||
my_string = my_name + "/" + my_string; | |||
} | |||
else | |||
{ | |||
ROS_ERROR("RosUI.h - function addVerticalSlider : No box name to use ! "); | |||
ROS_INFO("Vertical slider's callback will not be subscribed"); | |||
return; | |||
} | |||
// Subscription to sliders callback | |||
ros::Subscriber* h_slider = new ros::Subscriber(); | |||
*h_slider = nh_.subscribe<std_msgs::Float32> | |||
(my_string, queue_, boost::bind(&RosUI::sliderCallback, this, _1, zone)); | |||
count_++; | |||
} | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, | |||
FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
// Gets the numerical entry name and processes it to fit to ROS naming conventions | |||
std::string str = (std::string)label; | |||
std::string my_string = strProcess(str); | |||
// Builds the topic name from boxes and numerical entry names | |||
if (! box_names_.empty()) | |||
{ | |||
std::string my_name = ""; | |||
for (int i = 0; i<box_names_.size(); i++) | |||
{ | |||
if (box_names_[i] != "") | |||
{ | |||
my_name += "/" + strProcess(box_names_[i]); | |||
} | |||
else | |||
{ | |||
box_names_.erase(box_names_.begin()+i); | |||
} | |||
} | |||
my_string = my_name + "/" + my_string; | |||
} | |||
else | |||
{ | |||
ROS_ERROR("RosUI.h - function addVerticalSlider : No box name to use ! "); | |||
ROS_INFO("Vertical slider's callback will not be subscribed"); | |||
return; | |||
} | |||
// Subscription to numerical entries callback | |||
ros::Subscriber* num_entry = new ros::Subscriber(); | |||
*num_entry = nh_.subscribe<std_msgs::Float32>(my_string, queue_, | |||
boost::bind(&RosUI::numEntryCallback, this, _1, zone)); | |||
count_++; | |||
} | |||
// -- passive widgets | |||
// Nothing to say - not used | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{} | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{} | |||
// -- metadata declarations | |||
void declare(FAUSTFLOAT* zone, const char* key, const char* val) | |||
{ | |||
if (key=="ros") // We do not care if key is not "ros" here | |||
{ | |||
// Adds the Faust parameter's address (zone) to a zone vector | |||
// if a ros metadata has been declared | |||
zones_.push_back(zone); | |||
} | |||
} | |||
// Function returning the number of widgets added | |||
int getParamsCount() | |||
{ | |||
return count_; | |||
} | |||
// Function saying if, yes or no, there is any ROS metadata declared | |||
bool isTrue() | |||
{ | |||
if (!zones_.empty()) | |||
{ | |||
return true; // yes | |||
} | |||
else | |||
{ | |||
return false; // no | |||
} | |||
} | |||
// Function returning the Faust parameters addresses (zones) | |||
// if these zones correspond to metadata declared topics | |||
std::vector<FAUSTFLOAT*> getZones() | |||
{ | |||
return zones_; | |||
} | |||
private: | |||
ros::NodeHandle nh_; | |||
int queue_; | |||
int count_; | |||
bool meta_; | |||
std::vector<std::string> box_names_; | |||
std::vector<FAUSTFLOAT*> zones_; | |||
}; | |||
#endif |
@@ -0,0 +1,460 @@ | |||
/************************************************************************ | |||
************************************************************************ | |||
FAUST compiler | |||
Copyright (C) 2003-2015 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
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 2 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, write to the Free Software | |||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef SIMPLEPARSER_H | |||
#define SIMPLEPARSER_H | |||
// --------------------------------------------------------------------- | |||
// Simple Parser | |||
// A parser returns true if it was able to parse what it is | |||
// supposed to parse and advance the pointer. Otherwise it returns false | |||
// and the pointer is not advanced so that another parser can be tried. | |||
// --------------------------------------------------------------------- | |||
#include <vector> | |||
#include <map> | |||
#include <string> | |||
#include <fstream> | |||
#include <iostream> | |||
#include <ctype.h> | |||
using namespace std; | |||
struct itemInfo { | |||
std::string type; | |||
std::string label; | |||
std::string address; | |||
std::string init; | |||
std::string min; | |||
std::string max; | |||
std::string step; | |||
std::vector<std::pair<std::string, std::string> > meta; | |||
}; | |||
bool parseMenuList(const char*& p, vector<string>& names, vector<double>& values); | |||
bool parseMenuItem(const char*& p, string& name, double& value); | |||
void skipBlank(const char*& p); | |||
bool parseChar(const char*& p, char x); | |||
bool parseWord(const char*& p, const char* w); | |||
bool parseString(const char*& p, char quote, string& s); | |||
bool parseSQString(const char*& p, string& s); | |||
bool parseDQString(const char*& p, string& s); | |||
bool parseDouble(const char*& p, double& x); | |||
// --------------------------------------------------------------------- | |||
// | |||
// IMPLEMENTATION | |||
// | |||
// --------------------------------------------------------------------- | |||
/** | |||
* @brief parseMenuList, parse a menu list {'low' : 440.0; 'mid' : 880.0; 'hi' : 1760.0}... | |||
* @param p the string to parse, then the remaining string | |||
* @param names the vector of names found | |||
* @param values the vector of values found | |||
* @return true if a menu list was found | |||
*/ | |||
inline bool parseMenuList(const char*& p, vector<string>& names, vector<double>& values) | |||
{ | |||
vector<string> tmpnames; | |||
vector<double> tmpvalues; | |||
const char* saved = p; | |||
if (parseChar(p, '{')) { | |||
do { | |||
string n; | |||
double v; | |||
if (parseMenuItem(p, n, v)) { | |||
tmpnames.push_back(n); | |||
tmpvalues.push_back(v); | |||
} else { | |||
p = saved; | |||
return false; | |||
} | |||
} while (parseChar(p, ';')); | |||
if (parseChar(p, '}')) { | |||
// we suceeded | |||
names = tmpnames; | |||
values = tmpvalues; | |||
return true; | |||
} | |||
} | |||
p = saved; | |||
return false; | |||
} | |||
/** | |||
* @brief parseMenuItem, parse a menu item ...'low':440.0... | |||
* @param p the string to parse, then the remaining string | |||
* @param name the name found | |||
* @param value the value found | |||
* @return true if a nemu item was found | |||
*/ | |||
inline bool parseMenuItem(const char*& p, string& name, double& value) | |||
{ | |||
const char* saved = p; | |||
if (parseSQString(p, name) && parseChar(p, ':') && parseDouble(p, value)) { | |||
return true; | |||
} else { | |||
p = saved; | |||
return false; | |||
} | |||
} | |||
// --------------------------------------------------------------------- | |||
// Elementary parsers | |||
// --------------------------------------------------------------------- | |||
// Report a parsing error | |||
static bool parseError(const char*& p, const char* errmsg) | |||
{ | |||
std::cerr << "Parse error : " << errmsg << " here : " << p << std::endl; | |||
return true; | |||
} | |||
// Parse character x, but don't report error if fails | |||
static bool tryChar(const char*& p, char x) | |||
{ | |||
skipBlank(p); | |||
if (x == *p) { | |||
p++; | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
/** | |||
* @brief skipBlank : advance pointer p to the first non blank character | |||
* @param p the string to parse, then the remaining string | |||
*/ | |||
inline void skipBlank(const char*& p) | |||
{ | |||
while (isspace(*p)) { p++; } | |||
} | |||
/** | |||
* @brief parseChar : parse a specific character x | |||
* @param p the string to parse, then the remaining string | |||
* @param x the character to recognize | |||
* @return true if x was found at the begin of p | |||
*/ | |||
inline bool parseChar(const char*& p, char x) | |||
{ | |||
skipBlank(p); | |||
if (x == *p) { | |||
p++; | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
/** | |||
* @brief parseWord : parse a specific string w | |||
* @param p the string to parse, then the remaining string | |||
* @param w the string to recognize | |||
* @return true if string w was found at the begin of p | |||
*/ | |||
inline bool parseWord(const char*& p, const char* w) | |||
{ | |||
skipBlank(p); | |||
const char* saved = p; | |||
while ((*w == *p) && (*w)) {++w; ++p;} | |||
if (*w) { | |||
p = saved; | |||
return false; | |||
} else { | |||
return true; | |||
} | |||
} | |||
/** | |||
* @brief parseDouble : parse number [s]dddd[.dddd] and store the result in x | |||
* @param p the string to parse, then the remaining string | |||
* @param x the float number found if any | |||
* @return true if a float number was found at the begin of p | |||
*/ | |||
inline bool parseDouble(const char*& p, double& x) | |||
{ | |||
double sign = +1.0; // sign of the number | |||
double ipart = 0; // integral part of the number | |||
double dpart = 0; // decimal part of the number before division | |||
double dcoef = 1.0; // division factor for the decimal part | |||
bool valid = false; // true if the number contains at least one digit | |||
skipBlank(p); | |||
const char* saved = p; // to restore position if we fail | |||
if (parseChar(p, '+')) { | |||
sign = 1.0; | |||
} else if (parseChar(p, '-')) { | |||
sign = -1.0; | |||
} | |||
while (isdigit(*p)) { | |||
valid = true; | |||
ipart = ipart*10 + (*p - '0'); | |||
p++; | |||
} | |||
if (parseChar(p, '.')) { | |||
while (isdigit(*p)) { | |||
valid = true; | |||
dpart = dpart*10 + (*p - '0'); | |||
dcoef *= 10.0; | |||
p++; | |||
} | |||
} | |||
if (valid) { | |||
x = sign*(ipart + dpart/dcoef); | |||
} else { | |||
p = saved; | |||
} | |||
return valid; | |||
} | |||
/** | |||
* @brief parseString, parse an arbitrary quoted string q...q and store the result in s | |||
* @param p the string to parse, then the remaining string | |||
* @param quote the character used to quote the string | |||
* @param s the (unquoted) string found if any | |||
* @return true if a string was found at the begin of p | |||
*/ | |||
inline bool parseString(const char*& p, char quote, string& s) | |||
{ | |||
string str; | |||
skipBlank(p); | |||
const char* saved = p; | |||
if (*p++ == quote) { | |||
while ((*p != 0) && (*p != quote)) { | |||
str += *p++; | |||
} | |||
if (*p++ == quote) { | |||
s = str; | |||
return true; | |||
} | |||
} | |||
p = saved; | |||
return false; | |||
} | |||
/** | |||
* @brief parseSQString, parse a single quoted string '...' and store the result in s | |||
* @param p the string to parse, then the remaining string | |||
* @param s the (unquoted) string found if any | |||
* @return true if a string was found at the begin of p | |||
*/ | |||
inline bool parseSQString(const char*& p, string& s) | |||
{ | |||
return parseString(p, '\'', s); | |||
} | |||
/** | |||
* @brief parseDQString, parse a double quoted string "..." and store the result in s | |||
* @param p the string to parse, then the remaining string | |||
* @param s the (unquoted) string found if any | |||
* @return true if a string was found at the begin of p | |||
*/ | |||
inline bool parseDQString(const char*& p, string& s) | |||
{ | |||
return parseString(p, '"', s); | |||
} | |||
static bool parseMetaData(const char*& p, std::map<std::string, std::string>& metadatas) | |||
{ | |||
std::string metaKey, metaValue; | |||
if (parseChar(p, ':') && parseChar(p, '[')) { | |||
do { | |||
if (parseChar(p, '{') && parseDQString(p, metaKey) && parseChar(p, ':') && parseDQString(p, metaValue) && parseChar(p, '}')) { | |||
metadatas[metaKey] = metaValue; | |||
} | |||
} while (tryChar(p, ',')); | |||
return parseChar(p, ']'); | |||
} else { | |||
return false; | |||
} | |||
} | |||
static bool parseItemMetaData(const char*& p, std::vector<std::pair<std::string, std::string> >& metadatas) | |||
{ | |||
std::string metaKey, metaValue; | |||
if (parseChar(p, ':') && parseChar(p, '[')) { | |||
do { | |||
if (parseChar(p, '{') && parseDQString(p, metaKey) && parseChar(p, ':') && parseDQString(p, metaValue) && parseChar(p, '}')) { | |||
metadatas.push_back(std::make_pair(metaKey, metaValue)); | |||
} | |||
} while (tryChar(p, ',')); | |||
return parseChar(p, ']'); | |||
} else { | |||
return false; | |||
} | |||
} | |||
// --------------------------------------------------------------------- | |||
// Parse metadatas of the interface: | |||
// "name" : "...", "inputs" : "...", "outputs" : "...", ... | |||
// and store the result as key/value | |||
// | |||
static bool parseGlobalMetaData(const char*& p, std::string& key, std::string& value, std::map<std::string, std::string>& metadatas) | |||
{ | |||
if (parseDQString(p, key)) { | |||
if (key == "meta") { | |||
return parseMetaData(p, metadatas); | |||
} else { | |||
return parseChar(p, ':') && parseDQString(p, value); | |||
} | |||
} else { | |||
return false; | |||
} | |||
} | |||
// --------------------------------------------------------------------- | |||
// Parse gui: | |||
// "type" : "...", "label" : "...", "address" : "...", ... | |||
// and store the result in uiItems Vector | |||
// | |||
static bool parseUI(const char*& p, std::vector<itemInfo*>& uiItems, int& numItems) | |||
{ | |||
if (parseChar(p, '{')) { | |||
std::string label; | |||
std::string value; | |||
do { | |||
if (parseDQString(p, label)) { | |||
if (label == "type") { | |||
if (uiItems.size() != 0) { | |||
numItems++; | |||
} | |||
if (parseChar(p, ':') && parseDQString(p, value)) { | |||
itemInfo* item = new itemInfo; | |||
item->type = value; | |||
uiItems.push_back(item); | |||
} | |||
} | |||
else if (label == "label") { | |||
if (parseChar(p, ':') && parseDQString(p, value)) { | |||
itemInfo* item = uiItems[numItems]; | |||
item->label = value; | |||
} | |||
} | |||
else if (label == "address") { | |||
if (parseChar(p, ':') && parseDQString(p, value)) { | |||
itemInfo* item = uiItems[numItems]; | |||
item->address = value; | |||
} | |||
} | |||
else if (label == "meta") { | |||
itemInfo* item = uiItems[numItems]; | |||
if (!parseItemMetaData(p, item->meta)) { | |||
return false; | |||
} | |||
} | |||
else if (label == "init") { | |||
if (parseChar(p, ':') && parseDQString(p, value)) { | |||
itemInfo* item = uiItems[numItems]; | |||
item->init = value; | |||
} | |||
} | |||
else if (label == "min") { | |||
if (parseChar(p, ':') && parseDQString(p, value)) { | |||
itemInfo* item = uiItems[numItems]; | |||
item->min = value; | |||
} | |||
} | |||
else if (label == "max") { | |||
if (parseChar(p, ':') && parseDQString(p, value)) { | |||
itemInfo* item = uiItems[numItems]; | |||
item->max = value; | |||
} | |||
} | |||
else if (label == "step"){ | |||
if (parseChar(p, ':') && parseDQString(p, value)) { | |||
itemInfo* item = uiItems[numItems]; | |||
item->step = value; | |||
} | |||
} | |||
else if (label == "items") { | |||
if (parseChar(p, ':') && parseChar(p, '[')) { | |||
do { | |||
if (!parseUI(p, uiItems, numItems)) { | |||
return false; | |||
} | |||
} while (tryChar(p, ',')); | |||
if (parseChar(p, ']')) { | |||
itemInfo* item = new itemInfo; | |||
item->type = "close"; | |||
uiItems.push_back(item); | |||
numItems++; | |||
} | |||
} | |||
} | |||
} else { | |||
return false; | |||
} | |||
} while (tryChar(p, ',')); | |||
return parseChar(p, '}'); | |||
} else { | |||
return false; | |||
} | |||
} | |||
// --------------------------------------------------------------------- | |||
// Parse full JSON record describing a JSON/Faust interface : | |||
// {"metadatas": "...", "ui": [{ "type": "...", "label": "...", "items": [...], "address": "...","init": "...", "min": "...", "max": "...","step": "..."}]} | |||
// | |||
// and store the result in map Metadatas and vector containing the items of the interface. Returns true if parsing was successfull. | |||
// | |||
inline bool parseJson(const char*& p, std::map<std::string, std::string>& metadatas, std::vector<itemInfo*>& uiItems) | |||
{ | |||
parseChar(p, '{'); | |||
do { | |||
std::string key; | |||
std::string value; | |||
if (parseGlobalMetaData(p, key, value, metadatas)) { | |||
if (key != "meta") { | |||
// keep "name", "inputs", "outputs" key/value pairs | |||
metadatas[key] = value; | |||
} | |||
} else if (key == "ui") { | |||
int numItems = 0; | |||
parseChar(p, '[') && parseUI(p, uiItems, numItems); | |||
} | |||
} while (tryChar(p, ',')); | |||
return parseChar(p, '}'); | |||
} | |||
#endif // SIMPLEPARSER_H |
@@ -0,0 +1,5 @@ | |||
<!DOCTYPE RCC><RCC version="1.0"> | |||
<qresource> | |||
<file>Blue.qss</file> | |||
</qresource> | |||
</RCC> |
@@ -0,0 +1,177 @@ | |||
QPushButton{ | |||
min-width : 80px; | |||
border: 2px solid grey; | |||
border-radius: 6px; | |||
margin-top: 1ex; | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #811453, stop: 1 #702963); | |||
color: white; | |||
} | |||
QPushButton:hover{ | |||
border: 2px solid orange; | |||
} | |||
QPushButton:pressed{ | |||
background-color: orange; | |||
border-color: grey; | |||
} | |||
QGroupBox{ | |||
subcontrol: .QGroupBox; | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #003366, stop: 1 #22427C); | |||
margin-top: 3ex; | |||
border-radius: 5px; | |||
font-size: 10pt; | |||
font-weight: bold; | |||
color: white; | |||
} | |||
QGroupBox::title { | |||
subcontrol-origin: margin; | |||
subcontrol-position: top center; | |||
padding: 0 5px; | |||
color: white; | |||
} | |||
QMainWindow, QDialog{ | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #003366, stop: 1 #22427C); | |||
border-radius: 5px; | |||
margin-top: 3ex; | |||
font-size:10pt; | |||
font-weight:bold; | |||
color: white; | |||
} | |||
QPlainTextEdit, QTextEdit{ | |||
background-color: transparent; | |||
border: 2px solid #702963; | |||
border-radius: 5px; | |||
top: 3px; | |||
margin-top: 3ex; | |||
font-size:12pt; | |||
font-weight:bold; | |||
color: white; | |||
} | |||
QTextBrowser { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #003366, stop: 1 #22427C); | |||
color: white; | |||
} | |||
QTextBrowser:document{ | |||
text-decoration: underline; | |||
color: white; | |||
font: Menlo; | |||
font-size: 14px | |||
} | |||
QLabel{ | |||
color : white; | |||
background: transparent; | |||
} | |||
QSlider::groove:vertical { | |||
background: red; | |||
position: absolute; | |||
left: 13px; right: 13px; | |||
} | |||
QSlider::handle:vertical { | |||
height: 40px; | |||
width: 30px; | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #AAAAAA, stop : 0.05 #0A0A0A, stop: 0.3 #101010, stop : 0.90 #AAAAAA, stop: 0.91 #000000); | |||
margin: 0 -5px; | |||
border-radius: 5px; | |||
} | |||
QSlider::add-page:vertical { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 yellow, stop : 0.5 orange); | |||
} | |||
QSlider::sub-page:vertical { | |||
background: grey; | |||
} | |||
QSlider::groove:horizontal { | |||
background: red; | |||
position: absolute; | |||
top: 14px; bottom: 14px; | |||
} | |||
QSlider::handle:horizontal { | |||
width: 40px; | |||
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #6A455D, stop : 0.05 #811453, stop: 0.3 #811453, stop : 0.90 #6A455D, stop: 0.91 #702963); | |||
margin: -5px 0; | |||
border-radius: 5px; | |||
} | |||
QSlider::sub-page:horizontal { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 yellow, stop : 0.5 orange); | |||
} | |||
QSlider::add-page:horizontal { | |||
background: grey; | |||
} | |||
QTabWidget::pane { | |||
color : white; | |||
border-top: 2px #702963; | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #003366, stop: 1.0 #22427C); | |||
} | |||
QTabWidget::tab-bar { | |||
left: 5px; | |||
} | |||
QTabBar::tab { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #003366, stop: 0.4 #22427C, stop: 0.5 #003366, stop: 1.0 #22427C); | |||
border: 2px solid #808080; | |||
color : white; | |||
border-bottom-color: #702963; | |||
border-top-left-radius: 4px; | |||
border-top-right-radius: 4px; | |||
min-width: 8ex; | |||
padding: 2px; | |||
} | |||
QTabBar::tab:selected, QTabBar::tab:hover { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #003366, stop: 0.4 #22427C, stop: 0.5 #003366, stop: 1.0 #22427C); | |||
color : white; | |||
} | |||
QTabBar::tab:selected { | |||
color : white; | |||
border-color: #702963; | |||
border-bottom-color: #22427C; | |||
} | |||
QTabBar::tab:!selected { | |||
margin-top: 2px; | |||
} | |||
QListWidget{ | |||
background-color: transparent; | |||
} | |||
QListWidget::item{ | |||
color: white; | |||
} | |||
QStatusBar{ | |||
background-color: transparent; | |||
border: 0px; | |||
padding:0px 0px 0px 0px; | |||
margin:0px; | |||
} | |||
QScrollArea > QWidget > QWidget{ | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #003366, stop: 0.4 #22427C, stop: 0.5 #003366, stop: 1.0 #22427C); | |||
} | |||
QScrollArea > QWidget { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #003366, stop: 0.4 #22427C, stop: 0.5 #003366, stop: 1.0 #22427C); | |||
} | |||
QScrollArea { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #003366, stop: 0.4 #22427C, stop: 0.5 #003366, stop: 1.0 #22427C); | |||
} |
@@ -0,0 +1,5 @@ | |||
<!DOCTYPE RCC><RCC version="1.0"> | |||
<qresource> | |||
<file>Default.qss</file> | |||
</qresource> | |||
</RCC> |
@@ -0,0 +1,117 @@ | |||
QPushButton{ | |||
min-width : 80px; | |||
border: 2px solid grey; | |||
border-radius: 6px; | |||
margin-top: 1ex; | |||
border-color: #811453; | |||
background-color: transparent; | |||
} | |||
QPushButton:hover{ | |||
border: 2px; | |||
border-radius: 6px; | |||
border-color: #811453; | |||
background-color: #6A455D; | |||
} | |||
QPushButton:pressed{ | |||
background-color: #6A455D; | |||
border-radius: 6px; | |||
border-color: #811453; | |||
} | |||
QGroupBox{ | |||
subcontrol: .QGroupBox; | |||
margin-top: 3ex; | |||
font-size: 10pt; | |||
font-weight: bold; | |||
color: black; | |||
} | |||
QGroupBox::title { | |||
subcontrol-origin: margin; | |||
subcontrol-position: top center; | |||
padding: 0 5px; | |||
color: black; | |||
} | |||
QMainWindow { | |||
margin-top: 3ex; | |||
font-size:10pt; | |||
font-weight:bold; | |||
color: black; | |||
} | |||
QPlainTextEdit, QTextEdit{ | |||
background-color: transparent; | |||
border: 2px solid gray; | |||
border-radius: 5px; | |||
top: 3px; | |||
margin-top: 3ex; | |||
font-size:12pt; | |||
font-weight:bold; | |||
color: black; | |||
} | |||
QTextBrowser { | |||
color: black; | |||
} | |||
QTextBrowser:document{ | |||
text-decoration: underline; | |||
color: black; | |||
font: Menlo; | |||
font-size: 14px | |||
} | |||
QLabel{ | |||
color : black; | |||
background: transparent; | |||
} | |||
QSlider::groove:vertical { | |||
background: red; | |||
position: absolute; | |||
left: 13px; right: 13px; | |||
} | |||
QSlider::handle:vertical { | |||
height: 40px; | |||
width: 30px; | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #E67E30, stop : 0.05 #AD4F09, stop: 0.3 #E67E30, stop : 0.90 #AD4F09, stop: 0.91 #AD4F09); | |||
margin: 0 -5px; | |||
border-radius: 5px; | |||
} | |||
QSlider::add-page:vertical { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, | |||
stop: 0 #6A455D, stop : 0.5 #6A455D); | |||
} | |||
QSlider::sub-page:vertical { | |||
background: grey; | |||
} | |||
QSlider::groove:horizontal { | |||
background: red; | |||
position: absolute; | |||
top: 14px; bottom: 14px; | |||
} | |||
QSlider::handle:horizontal { | |||
width: 40px; | |||
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #E67E30, stop : 0.05 #AD4F09, stop: 0.3 #E67E30, stop : 0.90 #AD4F09, stop: 0.91 #AD4F09); | |||
margin: -5px 0; | |||
border-radius: 5px; | |||
} | |||
QSlider::sub-page:horizontal { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6A455D, stop : 0.5 #6A455D); | |||
} | |||
QSlider::add-page:horizontal { | |||
background: grey; | |||
} | |||
QListWidget{ | |||
background-color: transparent; | |||
} |
@@ -0,0 +1,5 @@ | |||
<!DOCTYPE RCC><RCC version="1.0"> | |||
<qresource> | |||
<file>Grey.qss</file> | |||
</qresource> | |||
</RCC> |
@@ -0,0 +1,174 @@ | |||
QPushButton { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0.8, y2: 0.8, | |||
stop: 0 #B0B0B0, stop: 1 #404040); | |||
min-width : 80px; | |||
border: 2px solid grey; | |||
border-radius: 6px; | |||
margin-top: 1ex; | |||
color:white | |||
} | |||
QPushButton:hover { | |||
border: 2px solid orange; | |||
} | |||
QPushButton:pressed { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #404040, stop: 1 #B0B0B0); | |||
} | |||
QGroupBox { | |||
subcontrol: .QGroupBox | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #A0A0A0, stop: 1 #202020); | |||
margin-top: 3ex; | |||
border-radius: 5px; | |||
font-size:10pt; | |||
font-weight:bold; | |||
color: white; | |||
} | |||
QGroupBox::title { | |||
subcontrol-origin: margin; | |||
subcontrol-position: top center; | |||
padding: 0 5px; | |||
color : white; | |||
} | |||
QMainWindow, QDialog { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #A0A0A0, stop: 1 #202020); | |||
border-radius: 5px; | |||
margin-top: 3ex; | |||
font-size:10pt; | |||
font-weight:bold; | |||
color: white; | |||
} | |||
QPlainTextEdit, QTextEdit{ | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #A0A0A0, stop: 1 #202020); | |||
border-color: yellow; | |||
border-radius: 5px; | |||
top: 3px; | |||
margin-top: 3ex; | |||
font-size:12pt; | |||
font-weight:bold; | |||
color: white; | |||
} | |||
QTextBrowser { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0.8, y2: 0.8, stop: 0 #A0A0A0, stop: 1 #202020); | |||
color: white; | |||
} | |||
QTextDocument{ | |||
text-decoration: underline; | |||
color: white; | |||
font: Menlo; | |||
font-size: 14px | |||
} | |||
QLabel{ | |||
color : white; | |||
background: transparent; | |||
} | |||
QSlider::groove:vertical { | |||
background: red; | |||
position: absolute; | |||
left: 13px; right: 13px; | |||
} | |||
QSlider::handle:vertical { | |||
height: 40px; | |||
width: 30px; | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #AAAAAA, stop : 0.05 #0A0A0A, stop: 0.3 #101010, stop : 0.90 #AAAAAA, stop: 0.91 #000000); | |||
margin: 0 -5px; | |||
border-radius: 5px; | |||
} | |||
QSlider::add-page:vertical { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 yellow, stop : 0.5 orange); | |||
} | |||
QSlider::sub-page:vertical { | |||
background: grey; | |||
} | |||
QSlider::groove:horizontal { | |||
background: red; | |||
position: absolute; | |||
top: 14px; bottom: 14px; | |||
} | |||
QSlider::handle:horizontal { | |||
width: 40px; | |||
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #AAAAAA, stop : 0.05 #0A0A0A, stop: 0.3 #101010, stop : 0.90 #AAAAAA, stop: 0.91 #000000); | |||
margin: -5px 0; | |||
border-radius: 5px; | |||
} | |||
QSlider::sub-page:horizontal { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 yellow, stop : 0.5 orange); | |||
} | |||
QSlider::add-page:horizontal { | |||
background: grey; | |||
} | |||
QTabWidget::pane { | |||
border-top: 2px solid orange; | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #A0A0A0, stop: 1 #202020); | |||
} | |||
QTabWidget::tab-bar { | |||
left: 5px; | |||
} | |||
QTabBar::tab { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #909090, stop: 0.4 #888888, stop: 0.5 #808080, stop: 1.0 #909090); | |||
border: 2px solid #808080; | |||
border-bottom-color: orange; | |||
border-top-left-radius: 4px; | |||
border-top-right-radius: 4px; | |||
min-width: 8ex; | |||
padding: 2px; | |||
} | |||
QTabBar::tab:selected, QTabBar::tab:hover { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #D0D0D0, stop: 0.4 #A0A0A0, stop: 0.5 #808080, stop: 1.0 #A0A0A0); | |||
} | |||
QTabBar::tab:selected { | |||
border-color: orange; | |||
border-bottom-color: #A0A0A0; | |||
} | |||
QTabBar::tab:!selected { | |||
margin-top: 2px; | |||
} | |||
QListWidget{ | |||
background-color: transparent; | |||
} | |||
QListWidget::item{ | |||
color: white; | |||
} | |||
QStatusBar{ | |||
background-color: transparent; | |||
border: 0px; | |||
padding:0px 0px 0px 0px; | |||
margin:0px; | |||
} | |||
QScrollArea > QWidget > QWidget{ | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0.8, y2: 0.8, stop: 0 #A0A0A0, stop: 1 #202020); | |||
} | |||
QScrollArea > QWidget { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0.8, y2: 0.8, stop: 0 #A0A0A0, stop: 1 #202020); | |||
} | |||
QScrollArea { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0.8, y2: 0.8, stop: 0 #A0A0A0, stop: 1 #202020); | |||
} |
@@ -0,0 +1,5 @@ | |||
<!DOCTYPE RCC><RCC version="1.0"> | |||
<qresource> | |||
<file>Salmon.qss</file> | |||
</qresource> | |||
</RCC> |
@@ -0,0 +1,171 @@ | |||
QPushButton{ | |||
background-color:#FF5E4D; | |||
min-width : 80px; | |||
border: 2px solid grey; | |||
border-radius: 6px; | |||
margin-top: 1ex; | |||
} | |||
QPushButton:hover{ | |||
border: 2px ; | |||
} | |||
QPushButton:pressed{ | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FFE4C4, stop: 1 #FEC3AC); | |||
} | |||
QGroupBox{ | |||
subcontrol: .QGroupBox | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #FFE4C4, stop: 1 #FEC3AC); | |||
border-radius: 5px; | |||
margin-top: 3ex; | |||
font-size:10pt; | |||
font-weight:bold; | |||
color: dark grey; | |||
color: white; | |||
} | |||
QGroupBox::title { | |||
subcontrol-origin: margin; | |||
subcontrol-position: top center; | |||
padding: 0 5px; | |||
color: black; | |||
} | |||
QMainWindow, QDialog { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #FFE4C4,stop: 1 #FEC3AC); | |||
border-radius: 5px; | |||
margin-top: 3ex; | |||
font-size:10pt; | |||
font-weight:bold; | |||
color: dark grey; | |||
color: white; | |||
} | |||
QPlainTextEdit, QTextEdit { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #FFE4C4,stop: 1 #FEC3AC); | |||
border: 2px solid gray; | |||
border-radius: 5px; | |||
top: 3px; | |||
margin-top: 3ex; | |||
font-size:12pt; | |||
font-weight:bold; | |||
color: black; | |||
} | |||
QTextBrowser { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #FFE4C4,stop: 1 #FEC3AC); | |||
color: black; | |||
} | |||
QTextBrowser:document{ | |||
text-decoration: underline; | |||
color: white; | |||
font: Menlo; | |||
font-size: 14px | |||
} | |||
QLabel{ | |||
color : black; | |||
background: transparent; | |||
} | |||
QSlider::groove:vertical { | |||
background: red; | |||
position: absolute; | |||
left: 13px; right: 13px; | |||
} | |||
QSlider::handle:vertical { | |||
height: 40px; | |||
width: 30px; | |||
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #4E3D28, stop : 0.05 #856D4D, stop: 0.1 #4E3D28, stop : 0.15 #856D4D, stop: 0.2 #4E3D28, stop : 0.25 #856D4D, stop: 0.3 #4E3D28, stop : 0.35 #856D4D, stop: 0.4 #4E3D28, stop : 0.45 #856D4D, stop: 0.5 #4E3D28, stop : 0.55 #856D4D, stop: 0.6 #4E3D28, stop : 0.65 #856D4D, stop: 0.7 #4E3D28, stop : 0.75 #856D4D, stop: 0.8 #4E3D28, stop : 0.85 #856D4D, stop: 0.95 #4E3D28); | |||
margin: 0 -5px; | |||
border-radius: 5px; | |||
} | |||
QSlider::add-page:vertical { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #FF5E4D, stop : 0.5 #FF5E4D); | |||
} | |||
QSlider::sub-page:vertical { | |||
background: #CECECE; | |||
} | |||
QSlider::groove:horizontal { | |||
background: red; | |||
position: absolute; | |||
top: 14px; bottom: 14px; | |||
} | |||
QSlider::handle:horizontal { | |||
width: 40px; | |||
background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #4E3D28, stop : 0.05 #856D4D, stop: 0.1 #4E3D28, stop : 0.15 #856D4D, stop: 0.2 #4E3D28, stop : 0.25 #856D4D, stop: 0.3 #4E3D28, stop : 0.35 #856D4D, stop: 0.4 #4E3D28, stop : 0.45 #856D4D, stop: 0.5 #4E3D28, stop : 0.55 #856D4D, stop: 0.6 #4E3D28, stop : 0.65 #856D4D, stop: 0.7 #4E3D28, stop : 0.75 #856D4D, stop: 0.8 #4E3D28, stop : 0.85 #856D4D, stop: 0.95 #4E3D28); | |||
margin: -5px 0; | |||
border-radius: 5px; | |||
} | |||
QSlider::sub-page:horizontal { | |||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FF5E4D stop : 0.5 #FF5E4D); | |||
} | |||
QSlider::add-page:horizontal { | |||
background: #CECECE; | |||
} | |||
QTabWidget::pane { | |||
color : black; | |||
border-top: 2px #FF5E4D; | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #FFE4C4,stop: 1 #FEC3AC); | |||
} | |||
QTabWidget::tab-bar { | |||
left: 5px; | |||
} | |||
QTabBar::tab { | |||
background: #FFE4C4; | |||
border: 2px solid #FF5E4D; | |||
color : black; | |||
border-bottom-color: #FF5E4D; | |||
border-top-left-radius: 4px; | |||
border-top-right-radius: 4px; | |||
min-width: 8ex; | |||
padding: 2px; | |||
} | |||
QTabBar::tab:selected, QTabBar::tab:hover { | |||
background: #FEC3AC; | |||
color : white; | |||
} | |||
QTabBar::tab:!selected { | |||
margin-top: 2px; | |||
} | |||
QListWidget{ | |||
background-color: transparent; | |||
} | |||
QListWidget::item{ | |||
color: color; | |||
} | |||
QStatusBar{ | |||
background-color: transparent; | |||
border: 0px; | |||
padding:0px 0px 0px 0px; | |||
margin:0px; | |||
} | |||
QScrollArea > QWidget > QWidget{ | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #FFE4C4,stop: 1 #FEC3AC); | |||
} | |||
QScrollArea > QWidget { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #FFE4C4,stop: 1 #FEC3AC); | |||
} | |||
QScrollArea { | |||
background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, stop: 0 #FFE4C4,stop: 1 #FEC3AC); | |||
} |
@@ -0,0 +1,117 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef FAUST_UI_H | |||
#define FAUST_UI_H | |||
#ifndef FAUSTFLOAT | |||
#define FAUSTFLOAT float | |||
#endif | |||
/******************************************************************************* | |||
* UI : Faust User Interface | |||
* This abstract class contains only the method that the faust compiler can | |||
* generate to describe a DSP interface. | |||
******************************************************************************/ | |||
class UI | |||
{ | |||
public: | |||
UI() {} | |||
virtual ~UI() {} | |||
// -- widget's layouts | |||
virtual void openTabBox(const char* label) = 0; | |||
virtual void openHorizontalBox(const char* label) = 0; | |||
virtual void openVerticalBox(const char* label) = 0; | |||
virtual void closeBox() = 0; | |||
// -- active widgets | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) = 0; | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) = 0; | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) = 0; | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) = 0; | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) = 0; | |||
// -- passive widgets | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) = 0; | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) = 0; | |||
// -- metadata declarations | |||
virtual void declare(FAUSTFLOAT*, const char*, const char*) {} | |||
}; | |||
//---------------------------------------------------------------- | |||
// Generic decorator | |||
//---------------------------------------------------------------- | |||
class DecoratorUI : public UI | |||
{ | |||
protected: | |||
UI* fUI; | |||
public: | |||
DecoratorUI(UI* ui = 0):fUI(ui) | |||
{} | |||
virtual ~DecoratorUI() { delete fUI; } | |||
// -- widget's layouts | |||
virtual void openTabBox(const char* label) { fUI->openTabBox(label); } | |||
virtual void openHorizontalBox(const char* label) { fUI->openHorizontalBox(label); } | |||
virtual void openVerticalBox(const char* label) { fUI->openVerticalBox(label); } | |||
virtual void closeBox() { fUI->closeBox(); } | |||
// -- active widgets | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) { fUI->addButton(label, zone); } | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) { fUI->addCheckButton(label, zone); } | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ fUI->addVerticalSlider(label, zone, init, min, max, step); } | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ fUI->addHorizontalSlider(label, zone, init, min, max, step); } | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ fUI->addNumEntry(label, zone, init, min, max, step); } | |||
// -- passive widgets | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ fUI->addHorizontalBargraph(label, zone, min, max); } | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ fUI->addVerticalBargraph(label, zone, min, max); } | |||
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) { fUI->declare(zone, key, val); } | |||
}; | |||
#endif |
@@ -0,0 +1,527 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __ValueConverter__ | |||
#define __ValueConverter__ | |||
/*************************************************************************************** | |||
ValueConverter.h | |||
(GRAME, © 2015) | |||
Set of conversion objects used to map user interface values (for example a gui slider | |||
delivering values between 0 and 1) to faust values (for example a vslider between | |||
20 and 20000) using a log scale. | |||
-- Utilities | |||
Range(lo,hi) : clip a value x between lo and hi | |||
Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2 | |||
Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2 | |||
-- Value Converters | |||
ValueConverter::ui2faust(x) | |||
ValueConverter::faust2ui(x) | |||
-- ValueConverters used for sliders depending of the scale | |||
LinearValueConverter(umin, umax, fmin, fmax) | |||
LogValueConverter(umin, umax, fmin, fmax) | |||
ExpValueConverter(umin, umax, fmin, fmax) | |||
-- ValueConverters used for accelerometers based on 3 points | |||
AccUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 0 | |||
AccDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 1 | |||
AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2 | |||
AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3 | |||
-- lists of ZoneControl are used to implement accelerometers metadata for each axes | |||
ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter | |||
-- ZoneReader are used to implement screencolor metadata | |||
ZoneReader(zone, valueConverter) : a zone with a data converter | |||
****************************************************************************************/ | |||
#include <float.h> | |||
#include <algorithm> // std::max | |||
#include <cmath> | |||
#include <vector> | |||
//-------------------------------------------------------------------------------------- | |||
// Interpolator(lo,hi,v1,v2) | |||
// Maps a value x between lo and hi to a value y between v1 and v2 | |||
// y = v1 + (x-lo)/(hi-lo)*(v2-v1) | |||
// y = v1 + (x-lo) * coef with coef = (v2-v1)/(hi-lo) | |||
// y = v1 + x*coef - lo*coef | |||
// y = v1 - lo*coef + x*coef | |||
// y = offset + x*coef with offset = v1 - lo*coef | |||
//-------------------------------------------------------------------------------------- | |||
class Interpolator | |||
{ | |||
private: | |||
//-------------------------------------------------------------------------------------- | |||
// Range(lo,hi) clip a value between lo and hi | |||
//-------------------------------------------------------------------------------------- | |||
struct Range | |||
{ | |||
double fLo; | |||
double fHi; | |||
Range(double x, double y) : fLo(std::min(x,y)), fHi(std::max(x,y)) {} | |||
double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; } | |||
}; | |||
Range fRange; | |||
double fCoef; | |||
double fOffset; | |||
public: | |||
Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi) | |||
{ | |||
if (hi != lo) { | |||
// regular case | |||
fCoef = (v2-v1)/(hi-lo); | |||
fOffset = v1 - lo*fCoef; | |||
} else { | |||
// degenerate case, avoids division by zero | |||
fCoef = 0; | |||
fOffset = (v1+v2)/2; | |||
} | |||
} | |||
double operator()(double v) | |||
{ | |||
double x = fRange(v); | |||
return fOffset + x*fCoef; | |||
} | |||
void getLowHigh(double& amin, double& amax) | |||
{ | |||
amin = fRange.fLo; | |||
amax = fRange.fHi; | |||
} | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Interpolator3pt(lo,mi,hi,v1,vm,v2) | |||
// Map values between lo mid hi to values between v1 vm v2 | |||
//-------------------------------------------------------------------------------------- | |||
class Interpolator3pt | |||
{ | |||
private: | |||
Interpolator fSegment1; | |||
Interpolator fSegment2; | |||
double fMid; | |||
public: | |||
Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) : | |||
fSegment1(lo, mi, v1, vm), | |||
fSegment2(mi, hi, vm, v2), | |||
fMid(mi) {} | |||
double operator()(double x) { return (x < fMid) ? fSegment1(x) : fSegment2(x); } | |||
void getMappingValues(double& amin, double& amid, double& amax) | |||
{ | |||
fSegment1.getLowHigh(amin, amid); | |||
fSegment2.getLowHigh(amid, amax); | |||
} | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Abstract ValueConverter class. Converts values between UI and Faust representations | |||
//-------------------------------------------------------------------------------------- | |||
class ValueConverter | |||
{ | |||
public: | |||
virtual ~ValueConverter() {} | |||
virtual double ui2faust(double x) = 0; | |||
virtual double faust2ui(double x) = 0; | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Linear conversion between ui and faust values | |||
//-------------------------------------------------------------------------------------- | |||
class LinearValueConverter : public ValueConverter | |||
{ | |||
private: | |||
Interpolator fUI2F; | |||
Interpolator fF2UI; | |||
public: | |||
LinearValueConverter(double umin, double umax, double fmin, double fmax) : | |||
fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax) | |||
{} | |||
LinearValueConverter() : | |||
fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.) | |||
{} | |||
virtual double ui2faust(double x) { return fUI2F(x); } | |||
virtual double faust2ui(double x) { return fF2UI(x); } | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Logarithmic conversion between ui and faust values | |||
//-------------------------------------------------------------------------------------- | |||
class LogValueConverter : public LinearValueConverter | |||
{ | |||
public: | |||
LogValueConverter(double umin, double umax, double fmin, double fmax) : | |||
LinearValueConverter(umin, umax, log(std::max(DBL_MIN,fmin)), log(std::max(DBL_MIN,fmax))) | |||
{} | |||
virtual double ui2faust(double x) { return exp(LinearValueConverter::ui2faust(x)); } | |||
virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(log(std::max(x, DBL_MIN))); } | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Exponential conversion between ui and Faust values | |||
//-------------------------------------------------------------------------------------- | |||
class ExpValueConverter : public LinearValueConverter | |||
{ | |||
public: | |||
ExpValueConverter(double umin, double umax, double fmin, double fmax) : | |||
LinearValueConverter(umin, umax, exp(fmin), exp(fmax)) | |||
{} | |||
virtual double ui2faust(double x) { return log(LinearValueConverter::ui2faust(x)); } | |||
virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(exp(x)); } | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// A converter than can be updated | |||
//-------------------------------------------------------------------------------------- | |||
class UpdatableValueConverter : public ValueConverter { | |||
protected: | |||
bool fActive; | |||
public: | |||
UpdatableValueConverter():fActive(true) | |||
{} | |||
virtual ~UpdatableValueConverter() | |||
{} | |||
virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0; | |||
virtual void getMappingValues(double& amin, double& amid, double& amax) = 0; | |||
void setActive(bool on_off) { fActive = on_off; } | |||
bool getActive() { return fActive; } | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Convert accelerometer or gyroscope values to Faust values | |||
// Using an Up curve (curve 0) | |||
//-------------------------------------------------------------------------------------- | |||
class AccUpConverter : public UpdatableValueConverter | |||
{ | |||
private: | |||
Interpolator3pt fA2F; | |||
Interpolator3pt fF2A; | |||
public: | |||
AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : | |||
fA2F(amin,amid,amax,fmin,fmid,fmax), | |||
fF2A(fmin,fmid,fmax,amin,amid,amax) | |||
{} | |||
virtual double ui2faust(double x) { return fA2F(x); } | |||
virtual double faust2ui(double x) { return fF2A(x); } | |||
virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) | |||
{ | |||
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); | |||
fA2F = Interpolator3pt(amin,amid,amax,fmin,fmid,fmax); | |||
fF2A = Interpolator3pt(fmin,fmid,fmax,amin,amid,amax); | |||
} | |||
virtual void getMappingValues(double& amin, double& amid, double& amax) | |||
{ | |||
fA2F.getMappingValues(amin, amid, amax); | |||
} | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Convert accelerometer or gyroscope values to Faust values | |||
// Using a Down curve (curve 1) | |||
//-------------------------------------------------------------------------------------- | |||
class AccDownConverter : public UpdatableValueConverter | |||
{ | |||
private: | |||
Interpolator3pt fA2F; | |||
Interpolator3pt fF2A; | |||
public: | |||
AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : | |||
fA2F(amin,amid,amax,fmax,fmid,fmin), | |||
fF2A(fmin,fmid,fmax,amax,amid,amin) | |||
{} | |||
virtual double ui2faust(double x) { return fA2F(x); } | |||
virtual double faust2ui(double x) { return fF2A(x); } | |||
virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) | |||
{ | |||
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); | |||
fA2F = Interpolator3pt(amin,amid,amax,fmax,fmid,fmin); | |||
fF2A = Interpolator3pt(fmin,fmid,fmax,amax,amid,amin); | |||
} | |||
virtual void getMappingValues(double& amin, double& amid, double& amax) | |||
{ | |||
fA2F.getMappingValues(amin, amid, amax); | |||
} | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Convert accelerometer or gyroscope values to Faust values | |||
// Using an Up-Down curve (curve 2) | |||
//-------------------------------------------------------------------------------------- | |||
class AccUpDownConverter : public UpdatableValueConverter | |||
{ | |||
private: | |||
Interpolator3pt fA2F; | |||
Interpolator fF2A; | |||
public: | |||
AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : | |||
fA2F(amin,amid,amax,fmin,fmax,fmin), | |||
fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotone function | |||
{} | |||
virtual double ui2faust(double x) { return fA2F(x); } | |||
virtual double faust2ui(double x) { return fF2A(x); } | |||
virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) | |||
{ | |||
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); | |||
fA2F = Interpolator3pt(amin,amid,amax,fmin,fmax,fmin); | |||
fF2A = Interpolator(fmin,fmax,amin,amax); | |||
} | |||
virtual void getMappingValues(double& amin, double& amid, double& amax) | |||
{ | |||
fA2F.getMappingValues(amin, amid, amax); | |||
} | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Convert accelerometer or gyroscope values to Faust values | |||
// Using a Down-Up curve (curve 3) | |||
//-------------------------------------------------------------------------------------- | |||
class AccDownUpConverter : public UpdatableValueConverter | |||
{ | |||
private: | |||
Interpolator3pt fA2F; | |||
Interpolator fF2A; | |||
public: | |||
AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : | |||
fA2F(amin,amid,amax,fmax,fmin,fmax), | |||
fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotone function | |||
{} | |||
virtual double ui2faust(double x) { return fA2F(x); } | |||
virtual double faust2ui(double x) { return fF2A(x); } | |||
virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) | |||
{ | |||
//__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); | |||
fA2F = Interpolator3pt(amin,amid,amax,fmax,fmin,fmax); | |||
fF2A = Interpolator(fmin,fmax,amin,amax); | |||
} | |||
virtual void getMappingValues(double& amin, double& amid, double& amax) | |||
{ | |||
fA2F.getMappingValues(amin, amid, amax); | |||
} | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Base class for ZoneControl | |||
//-------------------------------------------------------------------------------------- | |||
class ZoneControl | |||
{ | |||
protected: | |||
FAUSTFLOAT* fZone; | |||
public: | |||
ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {} | |||
virtual ~ZoneControl() {} | |||
virtual void update(double v) {} | |||
virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {} | |||
virtual void getMappingValues(double& amin, double& amid, double& amax) {} | |||
FAUSTFLOAT* getZone() { return fZone; } | |||
virtual void setActive(bool on_off) {} | |||
virtual bool getActive() { return false; } | |||
virtual int getCurve() { return -1; } | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Useful to implement accelerometers metadata as a list of ZoneControl for each axes | |||
//-------------------------------------------------------------------------------------- | |||
class ConverterZoneControl : public ZoneControl | |||
{ | |||
private: | |||
ValueConverter* fValueConverter; | |||
public: | |||
ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* valueConverter) : ZoneControl(zone), fValueConverter(valueConverter) {} | |||
virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere... | |||
void update(double v) { *fZone = fValueConverter->ui2faust(v); } | |||
ValueConverter* getConverter() { return fValueConverter; } | |||
}; | |||
//-------------------------------------------------------------------------------------- | |||
// Association of a zone and a four value converter, each one for each possible curve. | |||
// Useful to implement accelerometers metadata as a list of ZoneControl for each axes | |||
//-------------------------------------------------------------------------------------- | |||
class CurveZoneControl : public ZoneControl | |||
{ | |||
private: | |||
std::vector<UpdatableValueConverter*> fValueConverters; | |||
int fCurve; | |||
public: | |||
CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0) | |||
{ | |||
fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max)); | |||
fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max)); | |||
fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max)); | |||
fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max)); | |||
fCurve = curve; | |||
} | |||
virtual ~CurveZoneControl() | |||
{ | |||
std::vector<UpdatableValueConverter*>::iterator it; | |||
for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { | |||
delete(*it); | |||
} | |||
} | |||
void update(double v) { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); } | |||
void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) | |||
{ | |||
fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max); | |||
fCurve = curve; | |||
} | |||
void getMappingValues(double& amin, double& amid, double& amax) | |||
{ | |||
fValueConverters[fCurve]->getMappingValues(amin, amid, amax); | |||
} | |||
void setActive(bool on_off) | |||
{ | |||
std::vector<UpdatableValueConverter*>::iterator it; | |||
for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { | |||
(*it)->setActive(on_off); | |||
} | |||
} | |||
int getCurve() { return fCurve; } | |||
}; | |||
class ZoneReader | |||
{ | |||
private: | |||
FAUSTFLOAT* fZone; | |||
Interpolator fInterpolator; | |||
public: | |||
ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {} | |||
virtual ~ZoneReader() {} | |||
int getValue() { | |||
if (fZone != 0) { | |||
return (int)fInterpolator(*fZone); | |||
} else { | |||
return 127; | |||
} | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,287 @@ | |||
/************************************************************************ | |||
IMPORTANT NOTE : this file contains two clearly delimited sections : | |||
the ARCHITECTURE section (in two parts) and the USER section. Each section | |||
is governed by its own copyright and license. Please check individually | |||
each section for license and copyright information. | |||
*************************************************************************/ | |||
/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __faustconsole__ | |||
#define __faustconsole__ | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <unistd.h> | |||
#include <string.h> | |||
#include <stack> | |||
#include <string> | |||
#include <map> | |||
#include <vector> | |||
#include <iostream> | |||
#include "faust/gui/UI.h" | |||
/****************************************************************************** | |||
******************************************************************************* | |||
USER INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
struct param { | |||
FAUSTFLOAT* fZone; FAUSTFLOAT fMin; FAUSTFLOAT fMax; | |||
param(FAUSTFLOAT* z, FAUSTFLOAT init, FAUSTFLOAT a, FAUSTFLOAT b) : fZone(z), fMin(a), fMax(b) { *z = init; } | |||
}; | |||
class CMDUI : public UI | |||
{ | |||
int fArgc; | |||
char** fArgv; | |||
std::vector<char*> fFiles; | |||
std::stack<std::string> fPrefix; | |||
std::map<std::string, param> fKeyParam; | |||
void openAnyBox(const char* label) | |||
{ | |||
std::string prefix; | |||
if (label && label[0]) { | |||
prefix = fPrefix.top() + "-" + label; | |||
} else { | |||
prefix = fPrefix.top(); | |||
} | |||
fPrefix.push(prefix); | |||
} | |||
std::string simplify(const std::string& src) | |||
{ | |||
int i = 0; | |||
int level = 0; | |||
std::string dst; | |||
while (src[i] ) { | |||
switch (level) { | |||
case 0 : | |||
case 1 : | |||
case 2 : | |||
// Skip the begin of the label "--foo-" | |||
// until 3 '-' have been read | |||
if (src[i]=='-') { level++; } | |||
break; | |||
case 3 : | |||
// copy the content, but skip non alphnum | |||
// and content in parenthesis | |||
switch (src[i]) { | |||
case '(' : | |||
case '[' : | |||
level++; | |||
break; | |||
case '-' : | |||
dst += '-'; | |||
break; | |||
default : | |||
if (isalnum(src[i])) { | |||
dst+= tolower(src[i]); | |||
} | |||
} | |||
break; | |||
default : | |||
// here we are inside parenthesis and | |||
// we skip the content until we are back to | |||
// level 3 | |||
switch (src[i]) { | |||
case '(' : | |||
case '[' : | |||
level++; | |||
break; | |||
case ')' : | |||
case ']' : | |||
level--; | |||
break; | |||
default : | |||
break; | |||
} | |||
} | |||
i++; | |||
} | |||
return dst; | |||
} | |||
public: | |||
CMDUI(int argc, char *argv[]) : UI(), fArgc(argc), fArgv(argv) { fPrefix.push("-"); } | |||
virtual ~CMDUI() {} | |||
void addOption(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
std::string fullname = "-" + simplify(fPrefix.top() + "-" + label); | |||
fKeyParam.insert(make_pair(fullname, param(zone, init, min, max))); | |||
} | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
addOption(label,zone,0,0,1); | |||
} | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
addOption(label,zone,0,0,1); | |||
} | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addOption(label,zone,init,min,max); | |||
} | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addOption(label,zone,init,min,max); | |||
} | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addOption(label,zone,init,min,max); | |||
} | |||
// -- passive widgets | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) {} | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) {} | |||
virtual void openTabBox(const char* label) { openAnyBox(label); } | |||
virtual void openHorizontalBox(const char* label) { openAnyBox(label); } | |||
virtual void openVerticalBox(const char* label) { openAnyBox(label); } | |||
virtual void closeBox() { fPrefix.pop(); } | |||
virtual void show() {} | |||
virtual bool run() | |||
{ | |||
char c; | |||
printf("Type 'q' to quit\n"); | |||
while ((c = getchar()) != 'q') { | |||
sleep(1); | |||
} | |||
return true; | |||
} | |||
void printhelp_command() | |||
{ | |||
std::map<std::string, param>::iterator i; | |||
std::cout << fArgc << "\n"; | |||
std::cout << fArgv[0] << " option list : "; | |||
for (i = fKeyParam.begin(); i != fKeyParam.end(); i++) { | |||
std::cout << "[ " << i->first << " " << i->second.fMin << ".." << i->second.fMax <<" ] "; | |||
} | |||
std::cout << " infile outfile\n"; | |||
} | |||
void printhelp_init() | |||
{ | |||
std::map<std::string, param>::iterator i; | |||
std::cout << fArgc << "\n"; | |||
std::cout << fArgv[0] << " option list : "; | |||
for (i = fKeyParam.begin(); i != fKeyParam.end(); i++) { | |||
std::cout << "[ " << i->first << " " << i->second.fMin << ".." << i->second.fMax <<" ] "; | |||
} | |||
std::cout << std::endl; | |||
} | |||
void process_command() | |||
{ | |||
std::map<std::string, param>::iterator p; | |||
for (int i = 1; i < fArgc; i++) { | |||
if (fArgv[i][0] == '-') { | |||
if ((strcmp(fArgv[i], "-help") == 0) | |||
|| (strcmp(fArgv[i], "-h") == 0) | |||
|| (strcmp(fArgv[i], "--help") == 0)) { | |||
printhelp_command(); | |||
exit(1); | |||
} | |||
p = fKeyParam.find(fArgv[i]); | |||
if (p == fKeyParam.end()) { | |||
std::cout << fArgv[0] << " : unrecognized option " << fArgv[i] << "\n"; | |||
printhelp_command(); | |||
exit(1); | |||
} | |||
char* end; | |||
*(p->second.fZone) = FAUSTFLOAT(strtod(fArgv[i+1], &end)); | |||
i++; | |||
} else { | |||
fFiles.push_back(fArgv[i]); | |||
} | |||
} | |||
} | |||
unsigned long files() { return fFiles.size(); } | |||
char* file (int n) { return fFiles[n]; } | |||
char* input_file () { std::cout << "input file " << fFiles[0] << "\n"; return fFiles[0]; } | |||
char* output_file() { std::cout << "output file " << fFiles[1] << "\n"; return fFiles[1]; } | |||
void process_init() | |||
{ | |||
std::map<std::string, param>::iterator p; | |||
for (int i = 1; i < fArgc; i++) { | |||
if (fArgv[i][0] == '-') { | |||
if ((strcmp(fArgv[i], "-help") == 0) | |||
|| (strcmp(fArgv[i], "-h") == 0) | |||
|| (strcmp(fArgv[i], "--help") == 0)) { | |||
printhelp_init(); | |||
exit(1); | |||
} | |||
p = fKeyParam.find(fArgv[i]); | |||
if (p == fKeyParam.end()) { | |||
std::cout << fArgv[0] << " : unrecognized option " << fArgv[i] << "\n"; | |||
printhelp_init(); | |||
exit(1); | |||
} | |||
char* end; | |||
*(p->second.fZone) = FAUSTFLOAT(strtod(fArgv[i+1], &end)); | |||
i++; | |||
} | |||
} | |||
} | |||
}; | |||
#endif | |||
/********************END ARCHITECTURE SECTION (part 2/2)****************/ |
@@ -0,0 +1,366 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __httpdUI__ | |||
#define __httpdUI__ | |||
#include <iostream> | |||
#include <sstream> | |||
#include "faust/gui/HTTPDControler.h" | |||
#include "faust/gui/UI.h" | |||
#include "faust/gui/PathBuilder.h" | |||
#include "faust/misc.h" | |||
#ifndef _WIN32 | |||
#include <unistd.h> | |||
#include <pthread.h> | |||
#endif | |||
/****************************************************************************** | |||
******************************************************************************* | |||
HTTPD USER INTERFACE | |||
******************************************************************************* | |||
*******************************************************************************/ | |||
class httpdUIAux | |||
{ | |||
public: | |||
virtual bool run() = 0; | |||
virtual void stop() = 0; | |||
virtual int getTCPPort() = 0; | |||
virtual std::string getJSON() = 0; | |||
}; | |||
/* | |||
Note about URLs and the Faust UI names: | |||
---------------------------------------------------- | |||
Characters in a url could be: | |||
1. Reserved: ; / ? : @ & = + $ , | |||
These characters delimit URL parts. | |||
2. Unreserved: alphanum - _ . ! ~ * ' ( ) | |||
These characters have no special meaning and can be used as is. | |||
3. Excluded: control characters, space, < > # % ", { } | \ ^ [ ] ` | |||
To solve potential conflicts between the Faust UI objects naming scheme and | |||
the URL allowed characters, the reserved and excluded characters are replaced | |||
with '-' (hyphen). | |||
Space or tabulation are replaced with '_' (underscore) | |||
*/ | |||
class httpdServerUI : public UI, public httpdUIAux | |||
{ | |||
private: | |||
httpdfaust::HTTPDControler* fCtrl; | |||
const char* tr(const char* label) const | |||
{ | |||
static char buffer[1024]; | |||
char * ptr = buffer; int n = 1; | |||
while (*label && (n++ < 1024)) { | |||
switch (*label) { | |||
case ' ': case ' ': | |||
*ptr++ = '_'; | |||
break; | |||
case ';': case '/': case '?': case ':': case '@': | |||
case '&': case '=': case '+': case '$': case ',': | |||
case '<': case '>': case '#': case '%': case '"': | |||
case '{': case '}': case '|': case '\\': case '^': | |||
case '[': case ']': case '`': | |||
*ptr++ = '_'; | |||
break; | |||
default: | |||
*ptr++ = *label; | |||
} | |||
label++; | |||
} | |||
*ptr = 0; | |||
return buffer; | |||
} | |||
public: | |||
httpdServerUI(const char* applicationname, int inputs, int outputs, int argc, char* argv[], bool init = true) | |||
{ | |||
fCtrl = new httpdfaust::HTTPDControler(argc, argv, applicationname, init); | |||
fCtrl->setInputs(inputs); | |||
fCtrl->setOutputs(outputs); | |||
} | |||
virtual ~httpdServerUI() { delete fCtrl; } | |||
// -- widget's layouts | |||
virtual void openTabBox(const char* label) { fCtrl->opengroup("tgroup", tr(label)); } | |||
virtual void openHorizontalBox(const char* label) { fCtrl->opengroup("hgroup", tr(label)); } | |||
virtual void openVerticalBox(const char* label) { fCtrl->opengroup("vgroup", tr(label)); } | |||
virtual void closeBox() { fCtrl->closegroup(); } | |||
// -- active widgets | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) { fCtrl->addnode("button", tr(label), zone); } | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) { fCtrl->addnode("checkbox", tr(label), zone); } | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ fCtrl->addnode("vslider", tr(label), zone, init, min, max, step); } | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ fCtrl->addnode("hslider", tr(label), zone, init, min, max, step); } | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ fCtrl->addnode("nentry", tr(label), zone, init, min, max, step); } | |||
// -- passive widgets | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ fCtrl->addnode("hbargraph", tr(label), zone, min, max); } | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ fCtrl->addnode("vbargraph", tr(label), zone, min, max); } | |||
virtual void declare(FAUSTFLOAT*, const char* key, const char* val) { fCtrl->declare(key, val); } | |||
bool run() { fCtrl->run(); return true; } | |||
void stop() { fCtrl->stop(); } | |||
int getTCPPort() { return fCtrl->getTCPPort(); } | |||
std::string getJSON() { return fCtrl->getJSON(); } | |||
}; | |||
// API from sourcefetcher.hh and compiled in libHTTPDFaust library. | |||
int http_fetch(const char *url, char **fileBuf); | |||
/* | |||
Use to control a running Faust DSP wrapped with "httpdServerUI". | |||
*/ | |||
#ifndef _WIN32 | |||
class httpdClientUI : public GUI, public PathBuilder, public httpdUIAux | |||
{ | |||
private: | |||
class uiUrlValue : public uiItem | |||
{ | |||
private: | |||
std::string fPathURL; | |||
public: | |||
uiUrlValue(const std::string& path_url, GUI* ui, FAUSTFLOAT* zone) | |||
:uiItem(ui, zone),fPathURL(path_url) | |||
{} | |||
virtual ~uiUrlValue() | |||
{} | |||
virtual void reflectZone() | |||
{ | |||
FAUSTFLOAT v = *fZone; | |||
fCache = v; | |||
std::stringstream str; | |||
str << fPathURL << "?value=" << v; | |||
std::string path = str.str(); | |||
http_fetch(path.c_str(), NULL); | |||
} | |||
}; | |||
std::string fServerURL; | |||
std::string fJSON; | |||
std::map<std::string, FAUSTFLOAT*> fZoneMap; | |||
pthread_t fThread; | |||
int fTCPPort; | |||
bool fRunning; | |||
void insertMap(std::string label, FAUSTFLOAT* zone) | |||
{ | |||
fZoneMap[label] = zone; | |||
} | |||
static void* UpdateUI(void* arg) | |||
{ | |||
httpdClientUI* ui = static_cast<httpdClientUI*>(arg); | |||
std::map<std::string, FAUSTFLOAT*>::iterator it; | |||
while (ui->fRunning) { | |||
for (it = ui->fZoneMap.begin(); it != ui->fZoneMap.end(); it++) { | |||
char* answer; | |||
std::string path = (*it).first; | |||
http_fetch(path.c_str(), &answer); | |||
std::string answer_str = answer; | |||
(*(*it).second) = (FAUSTFLOAT)strtod(answer_str.substr(answer_str.find(' ')).c_str(), NULL); | |||
// 'http_fetch' result must be deallocated | |||
free(answer); | |||
} | |||
usleep(100000); | |||
} | |||
return 0; | |||
} | |||
virtual void addGeneric(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
std::string url = fServerURL + buildPath(label); | |||
insertMap(url, zone); | |||
new uiUrlValue(url, this, zone); | |||
} | |||
public: | |||
httpdClientUI(const std::string& server_url):fServerURL(server_url), fRunning(false) | |||
{ | |||
char* json_buffer = 0; | |||
std::string json_url = std::string(server_url) + "/JSON"; | |||
http_fetch(json_url.c_str(), &json_buffer); | |||
if (json_buffer) { | |||
fJSON = json_buffer; | |||
fTCPPort = atoi(server_url.substr(server_url.find_last_of(':') + 1).c_str()); | |||
// 'http_fetch' result must be deallocated | |||
free(json_buffer); | |||
std::cout << "Faust httpd client controling server '" << server_url << "'" << std::endl; | |||
} else { | |||
fJSON = ""; | |||
fTCPPort = -1; | |||
} | |||
} | |||
virtual ~httpdClientUI() | |||
{ | |||
stop(); | |||
} | |||
// -- widget's layouts | |||
void openTabBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
} | |||
void openHorizontalBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
} | |||
void openVerticalBox(const char* label) | |||
{ | |||
fControlsLevel.push_back(label); | |||
} | |||
void closeBox() | |||
{ | |||
fControlsLevel.pop_back(); | |||
} | |||
// -- active widgets | |||
virtual void addButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
// addGeneric(label, zone); | |||
// Do not update button state with received messages (otherwise on/off messages may be lost...) | |||
std::string url = fServerURL + buildPath(label); | |||
new uiUrlValue(url, this, zone); | |||
} | |||
virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) | |||
{ | |||
addGeneric(label, zone); | |||
} | |||
virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addGeneric(label, zone); | |||
} | |||
virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addGeneric(label, zone); | |||
} | |||
virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) | |||
{ | |||
addGeneric(label, zone); | |||
} | |||
// -- passive widgets | |||
virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
addGeneric(label, zone); | |||
} | |||
virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) | |||
{ | |||
addGeneric(label, zone); | |||
} | |||
virtual void declare(FAUSTFLOAT*, const char* key, const char* val) {} | |||
bool run() | |||
{ | |||
if (fTCPPort > 0) { | |||
fRunning = true; | |||
return (pthread_create(&fThread, NULL, UpdateUI, this) == 0); | |||
} else { | |||
return false; | |||
} | |||
} | |||
void stop() | |||
{ | |||
if (fRunning) { | |||
fRunning = false; | |||
pthread_join(fThread, NULL); | |||
} | |||
} | |||
int getTCPPort() | |||
{ | |||
return fTCPPort; | |||
} | |||
std::string getJSON() { return fJSON; } | |||
}; | |||
#endif | |||
/* | |||
Creates a httpdServerUI or httpdClientUI depending of the presence of '-server URL' parameter. | |||
*/ | |||
class httpdUI : public DecoratorUI | |||
{ | |||
public: | |||
httpdUI(const char* applicationname, int inputs, int outputs, int argc, char* argv[], bool init = true) | |||
{ | |||
if (argv && isopt(argv, "-server")) { | |||
#ifndef _WIN32 | |||
fUI = new httpdClientUI(lopts(argv, "-server", "http://localhost:5510")); | |||
#endif | |||
} else { | |||
fUI = new httpdServerUI(applicationname, inputs, outputs, argc, argv, init); | |||
} | |||
} | |||
bool run() { return dynamic_cast<httpdUIAux*>(fUI)->run(); } | |||
void stop() { dynamic_cast<httpdUIAux*>(fUI)->stop(); } | |||
int getTCPPort() { return dynamic_cast<httpdUIAux*>(fUI)->getTCPPort(); } | |||
std::string getJSON() { return dynamic_cast<httpdUIAux*>(fUI)->getJSON(); } | |||
}; | |||
#endif |
@@ -0,0 +1,82 @@ | |||
/* | |||
Copyright (C) 2012 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __jsonfaustui__ | |||
#define __jsonfaustui__ | |||
#include "faust/gui/meta.h" | |||
#include "faust/gui/UI.h" | |||
#include <string> | |||
namespace httpdfaust | |||
{ | |||
template <typename C> class jsonui; | |||
class jsonfaustui : public UI, public Meta | |||
{ | |||
jsonui<FAUSTFLOAT>* fJSON; | |||
public: | |||
jsonfaustui(const char *name, const char* address, int port); | |||
virtual ~jsonfaustui(); | |||
//-------------------------------------------- | |||
// UI methods | |||
//-------------------------------------------- | |||
// -- widget's layouts | |||
virtual void openTabBox(const char* label); | |||
virtual void openHorizontalBox(const char* label); | |||
virtual void openVerticalBox(const char* label); | |||
virtual void closeBox(); | |||
// -- active widgets | |||
void addButton(const char* label, FAUSTFLOAT* zone); | |||
void addCheckButton(const char* label, FAUSTFLOAT* zone); | |||
void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step); | |||
void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step); | |||
void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step); | |||
// -- passive widgets | |||
void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max); | |||
void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, float min, float max); | |||
// -- metadata declarations | |||
void declare(FAUSTFLOAT*, const char*, const char*); | |||
//-------------------------------------------- | |||
// additionnal methods (not part of UI) | |||
//-------------------------------------------- | |||
void numInput(int n); // should be called with the inputs number | |||
void numOutput(int n); // should be called with the outputs number | |||
void declare(const char* , const char*); // global metadata declaration | |||
//-------------------------------------------- | |||
// and eventually how to get the json as a string | |||
//-------------------------------------------- | |||
std::string json(bool flatten); | |||
}; | |||
} //end namespace | |||
#endif |
@@ -0,0 +1,31 @@ | |||
/************************************************************************ | |||
************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __meta__ | |||
#define __meta__ | |||
struct Meta | |||
{ | |||
virtual void declare(const char* key, const char* value) = 0; | |||
virtual ~Meta() {}; | |||
}; | |||
#endif |
@@ -0,0 +1,406 @@ | |||
/* | |||
Copyright (C) 2000 Paul Davis | |||
Copyright (C) 2003 Rohan Drape | |||
Copyright (C) 2016 GRAME (renaming for internal use) | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU Lesser General Public License as published by | |||
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||
ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code. | |||
This is safe for the case of one read thread and one write thread. | |||
*/ | |||
#ifndef __ring_buffer__ | |||
#define __ring_buffer__ | |||
#include <stdlib.h> | |||
#include <string.h> | |||
typedef struct { | |||
char *buf; | |||
size_t len; | |||
} | |||
ringbuffer_data_t; | |||
typedef struct { | |||
char *buf; | |||
volatile size_t write_ptr; | |||
volatile size_t read_ptr; | |||
size_t size; | |||
size_t size_mask; | |||
int mlocked; | |||
} | |||
ringbuffer_t; | |||
ringbuffer_t *ringbuffer_create(size_t sz); | |||
void ringbuffer_free(ringbuffer_t *rb); | |||
void ringbuffer_get_read_vector(const ringbuffer_t *rb, | |||
ringbuffer_data_t *vec); | |||
void ringbuffer_get_write_vector(const ringbuffer_t *rb, | |||
ringbuffer_data_t *vec); | |||
size_t ringbuffer_read(ringbuffer_t *rb, char *dest, size_t cnt); | |||
size_t ringbuffer_peek(ringbuffer_t *rb, char *dest, size_t cnt); | |||
void ringbuffer_read_advance(ringbuffer_t *rb, size_t cnt); | |||
size_t ringbuffer_read_space(const ringbuffer_t *rb); | |||
int ringbuffer_mlock(ringbuffer_t *rb); | |||
void ringbuffer_reset(ringbuffer_t *rb); | |||
void ringbuffer_reset_size (ringbuffer_t * rb, size_t sz); | |||
size_t ringbuffer_write(ringbuffer_t *rb, const char *src, | |||
size_t cnt); | |||
void ringbuffer_write_advance(ringbuffer_t *rb, size_t cnt); | |||
size_t ringbuffer_write_space(const ringbuffer_t *rb); | |||
/* Create a new ringbuffer to hold at least `sz' bytes of data. The | |||
actual buffer size is rounded up to the next power of two. */ | |||
inline ringbuffer_t * | |||
ringbuffer_create (size_t sz) | |||
{ | |||
size_t power_of_two; | |||
ringbuffer_t *rb; | |||
if ((rb = (ringbuffer_t *) malloc (sizeof (ringbuffer_t))) == NULL) { | |||
return NULL; | |||
} | |||
for (power_of_two = 1u; 1u << power_of_two < sz; power_of_two++); | |||
rb->size = 1u << power_of_two; | |||
rb->size_mask = rb->size; | |||
rb->size_mask -= 1; | |||
rb->write_ptr = 0; | |||
rb->read_ptr = 0; | |||
if ((rb->buf = (char *) malloc (rb->size)) == NULL) { | |||
free (rb); | |||
return NULL; | |||
} | |||
rb->mlocked = 0; | |||
return rb; | |||
} | |||
/* Free all data associated with the ringbuffer `rb'. */ | |||
inline void | |||
ringbuffer_free (ringbuffer_t * rb) | |||
{ | |||
#ifdef USE_MLOCK | |||
if (rb->mlocked) { | |||
munlock (rb->buf, rb->size); | |||
} | |||
#endif /* USE_MLOCK */ | |||
free (rb->buf); | |||
free (rb); | |||
} | |||
/* Lock the data block of `rb' using the system call 'mlock'. */ | |||
inline int | |||
ringbuffer_mlock (ringbuffer_t * rb) | |||
{ | |||
#ifdef USE_MLOCK | |||
if (mlock (rb->buf, rb->size)) { | |||
return -1; | |||
} | |||
#endif /* USE_MLOCK */ | |||
rb->mlocked = 1; | |||
return 0; | |||
} | |||
/* Reset the read and write pointers to zero. This is not thread | |||
safe. */ | |||
inline void | |||
ringbuffer_reset (ringbuffer_t * rb) | |||
{ | |||
rb->read_ptr = 0; | |||
rb->write_ptr = 0; | |||
memset(rb->buf, 0, rb->size); | |||
} | |||
/* Reset the read and write pointers to zero. This is not thread | |||
safe. */ | |||
inline void | |||
ringbuffer_reset_size (ringbuffer_t * rb, size_t sz) | |||
{ | |||
rb->size = sz; | |||
rb->size_mask = rb->size; | |||
rb->size_mask -= 1; | |||
rb->read_ptr = 0; | |||
rb->write_ptr = 0; | |||
} | |||
/* Return the number of bytes available for reading. This is the | |||
number of bytes in front of the read pointer and behind the write | |||
pointer. */ | |||
inline size_t | |||
ringbuffer_read_space (const ringbuffer_t * rb) | |||
{ | |||
size_t w, r; | |||
w = rb->write_ptr; | |||
r = rb->read_ptr; | |||
if (w > r) { | |||
return w - r; | |||
} else { | |||
return (w - r + rb->size) & rb->size_mask; | |||
} | |||
} | |||
/* Return the number of bytes available for writing. This is the | |||
number of bytes in front of the write pointer and behind the read | |||
pointer. */ | |||
inline size_t | |||
ringbuffer_write_space (const ringbuffer_t * rb) | |||
{ | |||
size_t w, r; | |||
w = rb->write_ptr; | |||
r = rb->read_ptr; | |||
if (w > r) { | |||
return ((r - w + rb->size) & rb->size_mask) - 1; | |||
} else if (w < r) { | |||
return (r - w) - 1; | |||
} else { | |||
return rb->size - 1; | |||
} | |||
} | |||
/* The copying data reader. Copy at most `cnt' bytes from `rb' to | |||
`dest'. Returns the actual number of bytes copied. */ | |||
inline size_t | |||
ringbuffer_read (ringbuffer_t * rb, char *dest, size_t cnt) | |||
{ | |||
size_t free_cnt; | |||
size_t cnt2; | |||
size_t to_read; | |||
size_t n1, n2; | |||
if ((free_cnt = ringbuffer_read_space (rb)) == 0) { | |||
return 0; | |||
} | |||
to_read = cnt > free_cnt ? free_cnt : cnt; | |||
cnt2 = rb->read_ptr + to_read; | |||
if (cnt2 > rb->size) { | |||
n1 = rb->size - rb->read_ptr; | |||
n2 = cnt2 & rb->size_mask; | |||
} else { | |||
n1 = to_read; | |||
n2 = 0; | |||
} | |||
memcpy (dest, &(rb->buf[rb->read_ptr]), n1); | |||
rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask; | |||
if (n2) { | |||
memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); | |||
rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask; | |||
} | |||
return to_read; | |||
} | |||
/* The copying data reader w/o read pointer advance. Copy at most | |||
`cnt' bytes from `rb' to `dest'. Returns the actual number of bytes | |||
copied. */ | |||
inline size_t | |||
ringbuffer_peek (ringbuffer_t * rb, char *dest, size_t cnt) | |||
{ | |||
size_t free_cnt; | |||
size_t cnt2; | |||
size_t to_read; | |||
size_t n1, n2; | |||
size_t tmp_read_ptr; | |||
tmp_read_ptr = rb->read_ptr; | |||
if ((free_cnt = ringbuffer_read_space (rb)) == 0) { | |||
return 0; | |||
} | |||
to_read = cnt > free_cnt ? free_cnt : cnt; | |||
cnt2 = tmp_read_ptr + to_read; | |||
if (cnt2 > rb->size) { | |||
n1 = rb->size - tmp_read_ptr; | |||
n2 = cnt2 & rb->size_mask; | |||
} else { | |||
n1 = to_read; | |||
n2 = 0; | |||
} | |||
memcpy (dest, &(rb->buf[tmp_read_ptr]), n1); | |||
tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask; | |||
if (n2) { | |||
memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2); | |||
} | |||
return to_read; | |||
} | |||
/* The copying data writer. Copy at most `cnt' bytes to `rb' from | |||
`src'. Returns the actual number of bytes copied. */ | |||
inline size_t | |||
ringbuffer_write (ringbuffer_t * rb, const char *src, size_t cnt) | |||
{ | |||
size_t free_cnt; | |||
size_t cnt2; | |||
size_t to_write; | |||
size_t n1, n2; | |||
if ((free_cnt = ringbuffer_write_space (rb)) == 0) { | |||
return 0; | |||
} | |||
to_write = cnt > free_cnt ? free_cnt : cnt; | |||
cnt2 = rb->write_ptr + to_write; | |||
if (cnt2 > rb->size) { | |||
n1 = rb->size - rb->write_ptr; | |||
n2 = cnt2 & rb->size_mask; | |||
} else { | |||
n1 = to_write; | |||
n2 = 0; | |||
} | |||
memcpy (&(rb->buf[rb->write_ptr]), src, n1); | |||
rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask; | |||
if (n2) { | |||
memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); | |||
rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask; | |||
} | |||
return to_write; | |||
} | |||
/* Advance the read pointer `cnt' places. */ | |||
inline void | |||
ringbuffer_read_advance (ringbuffer_t * rb, size_t cnt) | |||
{ | |||
size_t tmp = (rb->read_ptr + cnt) & rb->size_mask; | |||
rb->read_ptr = tmp; | |||
} | |||
/* Advance the write pointer `cnt' places. */ | |||
inline void | |||
ringbuffer_write_advance (ringbuffer_t * rb, size_t cnt) | |||
{ | |||
size_t tmp = (rb->write_ptr + cnt) & rb->size_mask; | |||
rb->write_ptr = tmp; | |||
} | |||
/* The non-copying data reader. `vec' is an array of two places. Set | |||
the values at `vec' to hold the current readable data at `rb'. If | |||
the readable data is in one segment the second segment has zero | |||
length. */ | |||
inline void | |||
ringbuffer_get_read_vector (const ringbuffer_t * rb, | |||
ringbuffer_data_t * vec) | |||
{ | |||
size_t free_cnt; | |||
size_t cnt2; | |||
size_t w, r; | |||
w = rb->write_ptr; | |||
r = rb->read_ptr; | |||
if (w > r) { | |||
free_cnt = w - r; | |||
} else { | |||
free_cnt = (w - r + rb->size) & rb->size_mask; | |||
} | |||
cnt2 = r + free_cnt; | |||
if (cnt2 > rb->size) { | |||
/* Two part vector: the rest of the buffer after the current write | |||
ptr, plus some from the start of the buffer. */ | |||
vec[0].buf = &(rb->buf[r]); | |||
vec[0].len = rb->size - r; | |||
vec[1].buf = rb->buf; | |||
vec[1].len = cnt2 & rb->size_mask; | |||
} else { | |||
/* Single part vector: just the rest of the buffer */ | |||
vec[0].buf = &(rb->buf[r]); | |||
vec[0].len = free_cnt; | |||
vec[1].len = 0; | |||
} | |||
} | |||
/* The non-copying data writer. `vec' is an array of two places. Set | |||
the values at `vec' to hold the current writeable data at `rb'. If | |||
the writeable data is in one segment the second segment has zero | |||
length. */ | |||
inline void | |||
ringbuffer_get_write_vector (const ringbuffer_t * rb, | |||
ringbuffer_data_t * vec) | |||
{ | |||
size_t free_cnt; | |||
size_t cnt2; | |||
size_t w, r; | |||
w = rb->write_ptr; | |||
r = rb->read_ptr; | |||
if (w > r) { | |||
free_cnt = ((r - w + rb->size) & rb->size_mask) - 1; | |||
} else if (w < r) { | |||
free_cnt = (r - w) - 1; | |||
} else { | |||
free_cnt = rb->size - 1; | |||
} | |||
cnt2 = w + free_cnt; | |||
if (cnt2 > rb->size) { | |||
/* Two part vector: the rest of the buffer after the current write | |||
ptr, plus some from the start of the buffer. */ | |||
vec[0].buf = &(rb->buf[w]); | |||
vec[0].len = rb->size - w; | |||
vec[1].buf = rb->buf; | |||
vec[1].len = cnt2 & rb->size_mask; | |||
} else { | |||
vec[0].buf = &(rb->buf[w]); | |||
vec[0].len = free_cnt; | |||
vec[1].len = 0; | |||
} | |||
} | |||
#endif // __ring_buffer__ |
@@ -0,0 +1,238 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __bela_midi__ | |||
#define __bela_midi__ | |||
#include <iostream> | |||
#include <cstdlib> | |||
#include "Midi.h" | |||
#include "faust/midi/midi.h" | |||
class MapUI; | |||
class bela_midi : public midi_handler { | |||
private: | |||
Midi fBelaMidi; | |||
static void midiCallback(MidiChannelMessage message, void* arg) | |||
{ | |||
bela_midi* midi = static_cast<bela_midi*>(arg); | |||
switch (message.getType()) { | |||
case kmmNoteOff: | |||
for (unsigned int i = 0; i < midi->fMidiInputs.size(); i++) { | |||
midi->fMidiInputs[i]->keyOff(0, message.getChannel(), message.getDataByte(0), message.getDataByte(1)); | |||
} | |||
break; | |||
case kmmNoteOn: | |||
for (unsigned int i = 0; i < midi->fMidiInputs.size(); i++) { | |||
if (message.getDataByte(1) != 0) { | |||
midi->fMidiInputs[i]->keyOn(0, message.getChannel(), message.getDataByte(0), message.getDataByte(1)); | |||
} else { | |||
midi->fMidiInputs[i]->keyOff(0, message.getChannel(), message.getDataByte(0), message.getDataByte(1)); | |||
} | |||
} | |||
break; | |||
case kmmPolyphonicKeyPressure: | |||
for (unsigned int i = 0; i < midi->fMidiInputs.size(); i++) { | |||
midi->fMidiInputs[i]->keyPress(0, message.getChannel(), message.getDataByte(0), message.getDataByte(1)); | |||
} | |||
break; | |||
case kmmControlChange: | |||
for (unsigned int i = 0; i < midi->fMidiInputs.size(); i++) { | |||
midi->fMidiInputs[i]->ctrlChange(0, message.getChannel(), message.getDataByte(0), message.getDataByte(1)); | |||
} | |||
break; | |||
case kmmProgramChange: | |||
for (unsigned int i = 0; i < midi->fMidiInputs.size(); i++) { | |||
midi->fMidiInputs[i]->progChange(0, message.getChannel(), message.getDataByte(0)); | |||
} | |||
break; | |||
case kmmChannelPressure: | |||
for (unsigned int i = 0; i < midi->fMidiInputs.size(); i++) { | |||
midi->fMidiInputs[i]->chanPress(0, message.getChannel(), message.getDataByte(0)); | |||
} | |||
break; | |||
case kmmPitchBend: | |||
for (unsigned int i = 0; i < midi->fMidiInputs.size(); i++) { | |||
midi->fMidiInputs[i]->pitchWheel(0, message.getChannel(), ((message.getDataByte(1) * 128.0 + message.getDataByte(0)) - 8192) / 8192.0); | |||
} | |||
break; | |||
case kmmNone: | |||
case kmmAny: | |||
default: | |||
break; | |||
} | |||
} | |||
public: | |||
bela_midi() | |||
:midi_handler("bela") | |||
{} | |||
virtual ~bela_midi() | |||
{ | |||
stop_midi(); | |||
} | |||
bool start_midi() | |||
{ | |||
if (fBelaMidi.readFrom(0) < 0) { | |||
return false; | |||
} | |||
if (fBelaMidi.writeTo(0) < 0) { | |||
return false; | |||
} | |||
fBelaMidi.enableParser(true); | |||
fBelaMidi.setParserCallback(midiCallback, this); | |||
return true; | |||
} | |||
void stop_midi() | |||
{ | |||
// Nothing todo? | |||
} | |||
MapUI* keyOn(int channel, int pitch, int velocity) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_NOTE_ON + channel), | |||
static_cast<unsigned char>(pitch), | |||
static_cast<unsigned char>(velocity) }; | |||
fBelaMidi.writeOutput(buffer, 3); | |||
return 0; | |||
} | |||
void keyOff(int channel, int pitch, int velocity) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_NOTE_OFF + channel), | |||
static_cast<unsigned char>(pitch), | |||
static_cast<unsigned char>(velocity) }; | |||
fBelaMidi.writeOutput(buffer, 3); | |||
} | |||
void ctrlChange(int channel, int ctrl, int val) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_CONTROL_CHANGE + channel), | |||
static_cast<unsigned char>(ctrl), | |||
static_cast<unsigned char>(val) }; | |||
fBelaMidi.writeOutput(buffer, 3); | |||
} | |||
void chanPress(int channel, int press) | |||
{ | |||
unsigned char buffer[2] | |||
= { static_cast<unsigned char>(MIDI_AFTERTOUCH + channel), | |||
static_cast<unsigned char>(press) }; | |||
fBelaMidi.writeOutput(buffer, 2); | |||
} | |||
void progChange(int channel, int pgm) | |||
{ | |||
unsigned char buffer[2] | |||
= { static_cast<unsigned char>(MIDI_PROGRAM_CHANGE + channel), | |||
static_cast<unsigned char>(pgm) }; | |||
fBelaMidi.writeOutput(buffer, 2); | |||
} | |||
void keyPress(int channel, int pitch, int press) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_POLY_AFTERTOUCH + channel), | |||
static_cast<unsigned char>(pitch), | |||
static_cast<unsigned char>(press) }; | |||
fBelaMidi.writeOutput(buffer, 3); | |||
} | |||
void pitchWheel(int channel, int wheel) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_PITCH_BEND + channel), | |||
static_cast<unsigned char>(wheel & 0x7F), | |||
static_cast<unsigned char>((wheel >> 7) & 0x7F) }; | |||
fBelaMidi.writeOutput(buffer, 3); | |||
} | |||
void ctrlChange14bits(int channel, int ctrl, int value) {} | |||
void start_sync(double date) | |||
{ | |||
unsigned char buffer[1] = { MIDI_START }; | |||
fBelaMidi.writeOutput(buffer, 1); | |||
} | |||
void stop_sync(double date) | |||
{ | |||
unsigned char buffer[1] = { MIDI_STOP }; | |||
fBelaMidi.writeOutput(buffer, 1); | |||
} | |||
void clock(double date) | |||
{ | |||
unsigned char buffer[1] = { MIDI_CLOCK }; | |||
fBelaMidi.writeOutput(buffer, 1); | |||
} | |||
}; | |||
#endif // __bela_midi__ | |||
/* | |||
// Use case example | |||
#include "faust/gui/MidiUI.h" | |||
#include "faust/midi/bela-midi.h" | |||
bela_midi fMIDI; | |||
MidiUI* fMidiUI; | |||
bool setup(BeagleRTContext *context, void *userData) | |||
{ | |||
.... | |||
// Setup a generic MidiUI to use bela_midi MIDI handler | |||
fMidiUI = new MidiUI(&fMIDI); | |||
// Add MidiUI control interface to DSP | |||
fDSP.buildUserInterface(fMidiUI); | |||
// And run it... | |||
fMidiUI->run(); | |||
.... | |||
} | |||
void cleanup(BeagleRTContext *context, void *userData) | |||
{ | |||
.... | |||
delete fMidiUI; | |||
} | |||
*/ |
@@ -0,0 +1,203 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __jack_midi__ | |||
#define __jack_midi__ | |||
#include <iostream> | |||
#include <cstdlib> | |||
#include <jack/midiport.h> | |||
#include "faust/midi/midi.h" | |||
#include "faust/gui/ring-buffer.h" | |||
class MapUI; | |||
class jack_midi_handler : public midi_handler { | |||
protected: | |||
ringbuffer_t* fOutBuffer; | |||
void writeMessage(unsigned char* buffer, size_t size) | |||
{ | |||
if (fOutBuffer) { | |||
size_t res; | |||
// Write size of message | |||
if ((res = ringbuffer_write(fOutBuffer, (const char*)&size, sizeof(size_t))) != sizeof(size_t)) { | |||
std::cerr << "writeMessage size : error size = " << size << " res = " << res << std::endl; | |||
} | |||
// Write message content | |||
if ((res = ringbuffer_write(fOutBuffer, (const char*)buffer, size)) != size) { | |||
std::cerr << "writeMessage size : error size = " << size << " res = " << res << std::endl; | |||
} | |||
} | |||
} | |||
void processMidiInBuffer(void* port_buf_in) | |||
{ | |||
for (int i = 0; i < jack_midi_get_event_count(port_buf_in); ++i) { | |||
jack_midi_event_t event; | |||
if (jack_midi_event_get(&event, port_buf_in, i) == 0) { | |||
size_t nBytes = event.size; | |||
int type = (int)event.buffer[0] & 0xf0; | |||
int channel = (int)event.buffer[0] & 0x0f; | |||
double time = event.time; // Timestamp in frames | |||
// MIDI sync | |||
if (nBytes == 1) { | |||
handleSync(time, (int)event.buffer[0]); | |||
} else if (nBytes == 2) { | |||
handleData1(time, type, channel, (int)event.buffer[1]); | |||
} else if (nBytes == 3) { | |||
handleData2(time, type, channel, (int)event.buffer[1], (int)event.buffer[2]); | |||
} | |||
} | |||
} | |||
} | |||
void processMidiOutBuffer(void* port_buf_out_aux, bool reset = false) | |||
{ | |||
// MIDI output | |||
unsigned char* port_buf_out = (unsigned char*)port_buf_out_aux; | |||
if (reset) { | |||
jack_midi_reset_buffer(port_buf_out); | |||
} else { | |||
jack_midi_clear_buffer(port_buf_out); | |||
} | |||
size_t res, message_size; | |||
// Write each message one by one | |||
while (ringbuffer_read(fOutBuffer, (char*)&message_size, sizeof(message_size)) == sizeof(message_size)) { | |||
// Reserve MIDI event with the correct size | |||
jack_midi_data_t* data = jack_midi_event_reserve(port_buf_out, 0, message_size); | |||
if (data) { | |||
// Write its content | |||
if ((res = ringbuffer_read(fOutBuffer, (char*)data, message_size)) != message_size) { | |||
std::cerr << "processMidiOut incorrect message : res = " << res << std::endl; | |||
} | |||
} else { | |||
std::cerr << "jack_midi_event_reserve error" << std::endl; | |||
} | |||
} | |||
} | |||
public: | |||
jack_midi_handler(const std::string& name = "JACKHandler") | |||
:midi_handler(name) | |||
{ | |||
fOutBuffer = ringbuffer_create(8192); | |||
} | |||
virtual ~jack_midi_handler() | |||
{ | |||
ringbuffer_free(fOutBuffer); | |||
} | |||
MapUI* keyOn(int channel, int pitch, int velocity) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_NOTE_ON + channel), | |||
static_cast<unsigned char>(pitch), | |||
static_cast<unsigned char>(velocity) }; | |||
writeMessage(buffer, 3); | |||
return 0; | |||
} | |||
void keyOff(int channel, int pitch, int velocity) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_NOTE_OFF + channel), | |||
static_cast<unsigned char>(pitch), | |||
static_cast<unsigned char>(velocity) }; | |||
writeMessage(buffer, 3); | |||
} | |||
void ctrlChange(int channel, int ctrl, int val) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_CONTROL_CHANGE + channel), | |||
static_cast<unsigned char>(ctrl), | |||
static_cast<unsigned char>(val) }; | |||
writeMessage(buffer, 3); | |||
} | |||
void chanPress(int channel, int press) | |||
{ | |||
unsigned char buffer[2] | |||
= { static_cast<unsigned char>(MIDI_AFTERTOUCH + channel), | |||
static_cast<unsigned char>(press) }; | |||
writeMessage(buffer, 2); | |||
} | |||
void progChange(int channel, int pgm) | |||
{ | |||
unsigned char buffer[2] | |||
= { static_cast<unsigned char>(MIDI_PROGRAM_CHANGE + channel), | |||
static_cast<unsigned char>(pgm) }; | |||
writeMessage(buffer, 2); | |||
} | |||
void keyPress(int channel, int pitch, int press) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_POLY_AFTERTOUCH + channel), | |||
static_cast<unsigned char>(pitch), | |||
static_cast<unsigned char>(press) }; | |||
writeMessage(buffer, 3); | |||
} | |||
void pitchWheel(int channel, int wheel) | |||
{ | |||
unsigned char buffer[3] | |||
= { static_cast<unsigned char>(MIDI_PITCH_BEND + channel), | |||
static_cast<unsigned char>(wheel & 0x7F), | |||
static_cast<unsigned char>((wheel >> 7) & 0x7F) }; | |||
writeMessage(buffer, 3); | |||
} | |||
void ctrlChange14bits(int channel, int ctrl, int value) {} | |||
void start_sync(double date) | |||
{ | |||
unsigned char buffer[1] = { MIDI_START }; | |||
writeMessage(buffer, 1); | |||
} | |||
void stop_sync(double date) | |||
{ | |||
unsigned char buffer[1] = { MIDI_STOP }; | |||
writeMessage(buffer, 1); | |||
} | |||
void clock(double date) | |||
{ | |||
unsigned char buffer[1] = { MIDI_CLOCK }; | |||
writeMessage(buffer, 1); | |||
} | |||
}; | |||
#endif |
@@ -0,0 +1,199 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __juce_midi__ | |||
#define __juce_midi__ | |||
#include "juce_MidiMessage.h" | |||
#include "faust/midi/midi.h" | |||
class MapUI; | |||
struct juce_midi_handler : public midi_handler { | |||
juce_midi_handler():midi_handler("JUCE") | |||
{} | |||
void decodeBuffer(MidiBuffer& midiMessages) | |||
{ | |||
MidiMessage msg; | |||
int ignore; | |||
for (MidiBuffer::Iterator it(midiMessages); it.getNextEvent(msg, ignore);) { | |||
decodeMessage(msg); | |||
} | |||
} | |||
void decodeMessage(const MidiMessage& message) | |||
{ | |||
const uint8* data = message.getRawData(); | |||
if (message.isNoteOff()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->keyOff(0, message.getChannel(), data[1], data[2]); | |||
} | |||
} else if (message.isNoteOn()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
if (data[1] != 0) { | |||
fMidiInputs[i]->keyOn(0, message.getChannel(), data[1], data[2]); | |||
} else { | |||
fMidiInputs[i]->keyOff(0, message.getChannel(), data[1], data[2]); | |||
} | |||
} | |||
} else if (message.isAftertouch()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->keyPress(0, message.getChannel(), data[1], data[2]); | |||
} | |||
} else if (message.isController()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->ctrlChange(0, message.getChannel(), data[1], data[2]); | |||
} | |||
} else if (message.isProgramChange()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->progChange(0, message.getChannel(), data[1]); | |||
} | |||
} else if (message.isChannelPressure()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->chanPress(0, message.getChannel(), data[1]); | |||
} | |||
} else if (message.isPitchWheel()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->pitchWheel(0, message.getChannel(), ((data[1] * 128.0 + data[2]) - 8192) / 8192.0); | |||
} | |||
} else if (message.isMidiClock()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->clock(message.getTimeStamp()); | |||
} | |||
} else if (message.isMidiStart()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->start_sync(message.getTimeStamp()); | |||
} | |||
} else if (message.isMidiStop()) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->stop_sync(message.getTimeStamp()); | |||
} | |||
} else { | |||
std::cerr << "Unused MIDI message" << std::endl; | |||
} | |||
} | |||
}; | |||
class juce_midi : public juce_midi_handler, public MidiInputCallback { | |||
private: | |||
MidiInput* fMidiIn; | |||
MidiOutput* fMidiOut; | |||
void handleIncomingMidiMessage(MidiInput*, const MidiMessage& message) | |||
{ | |||
decodeMessage(message); | |||
} | |||
public: | |||
virtual ~juce_midi() | |||
{ | |||
stop_midi(); | |||
} | |||
bool start_midi() | |||
{ | |||
if ((fMidiIn = MidiInput::openDevice(MidiInput::getDefaultDeviceIndex(), this)) == nullptr) { | |||
return false; | |||
} | |||
if ((fMidiOut = MidiOutput::openDevice(MidiOutput::getDefaultDeviceIndex())) == nullptr) { | |||
return false; | |||
} | |||
fMidiIn->start(); | |||
return true; | |||
} | |||
void stop_midi() | |||
{ | |||
fMidiIn->stop(); | |||
delete fMidiIn; | |||
delete fMidiOut; | |||
} | |||
MapUI* keyOn(int channel, int pitch, int velocity) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::noteOn(channel + 1, pitch, uint8(velocity))); | |||
return 0; | |||
} | |||
void keyOff(int channel, int pitch, int velocity) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::noteOff(channel + 1, pitch, uint8(velocity))); | |||
} | |||
void ctrlChange(int channel, int ctrl, int val) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::controllerEvent(channel + 1, ctrl, uint8(val))); | |||
} | |||
void chanPress(int channel, int press) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::channelPressureChange(channel + 1, press)); | |||
} | |||
void progChange(int channel, int pgm) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::programChange(channel + 1, pgm)); | |||
} | |||
void keyPress(int channel, int pitch, int press) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::aftertouchChange(channel + 1, pitch, press)); | |||
} | |||
void pitchWheel(int channel, int wheel) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::pitchWheel(channel + 1, wheel)); | |||
} | |||
void ctrlChange14bits(int channel, int ctrl, int value) {} | |||
void start_sync(double date) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::midiStart()); | |||
} | |||
void stop_sync(double date) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::midiStop()); | |||
} | |||
void clock(double date) | |||
{ | |||
fMidiOut->sendMessageNow(MidiMessage::midiClock()); | |||
} | |||
}; | |||
#endif // __juce_midi__ | |||
@@ -0,0 +1,212 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __midi__ | |||
#define __midi__ | |||
#include <vector> | |||
#include <string> | |||
#include <algorithm> | |||
class MapUI; | |||
//---------------------------------------------------------------- | |||
// MIDI processor definition | |||
//---------------------------------------------------------------- | |||
class midi { | |||
public: | |||
midi() {} | |||
virtual ~midi() {} | |||
// Additional time-stamped API for MIDI input | |||
virtual MapUI* keyOn(double, int channel, int pitch, int velocity) | |||
{ | |||
return keyOn(channel, pitch, velocity); | |||
} | |||
virtual void keyOff(double, int channel, int pitch, int velocity = 127) | |||
{ | |||
keyOff(channel, pitch, velocity); | |||
} | |||
virtual void pitchWheel(double, int channel, int wheel) | |||
{ | |||
pitchWheel(channel, wheel); | |||
} | |||
virtual void ctrlChange(double, int channel, int ctrl, int value) | |||
{ | |||
ctrlChange(channel, ctrl, value); | |||
} | |||
virtual void progChange(double, int channel, int pgm) | |||
{ | |||
progChange(channel, pgm); | |||
} | |||
virtual void keyPress(double, int channel, int pitch, int press) | |||
{ | |||
keyPress(channel, pitch, press); | |||
} | |||
virtual void chanPress(double date, int channel, int press) | |||
{ | |||
chanPress(channel, press); | |||
} | |||
virtual void ctrlChange14bits(double, int channel, int ctrl, int value) | |||
{ | |||
ctrlChange14bits(channel, ctrl, value); | |||
} | |||
// MIDI sync | |||
virtual void start_sync(double date) {} | |||
virtual void stop_sync(double date) {} | |||
virtual void clock(double date) {} | |||
// Standard MIDI API | |||
virtual MapUI* keyOn(int channel, int pitch, int velocity) { return 0; } | |||
virtual void keyOff(int channel, int pitch, int velocity) {} | |||
virtual void keyPress(int channel, int pitch, int press) {} | |||
virtual void chanPress(int channel, int press) {} | |||
virtual void ctrlChange(int channel, int ctrl, int value) {} | |||
virtual void ctrlChange14bits(int channel, int ctrl, int value) {} | |||
virtual void pitchWheel(int channel, int wheel) {} | |||
virtual void progChange(int channel, int pgm) {} | |||
enum MidiStatus { | |||
// channel voice messages | |||
MIDI_NOTE_OFF = 0x80, | |||
MIDI_NOTE_ON = 0x90, | |||
MIDI_CONTROL_CHANGE = 0xB0, | |||
MIDI_PROGRAM_CHANGE = 0xC0, | |||
MIDI_PITCH_BEND = 0xE0, | |||
MIDI_AFTERTOUCH = 0xD0, // aka channel pressure | |||
MIDI_POLY_AFTERTOUCH = 0xA0, // aka key pressure | |||
MIDI_CLOCK = 0xF8, | |||
MIDI_START = 0xFA, | |||
MIDI_STOP = 0xFC | |||
}; | |||
enum MidiCtrl { | |||
ALL_NOTES_OFF = 123, | |||
ALL_SOUND_OFF = 120 | |||
}; | |||
}; | |||
//---------------------------------------------------------------- | |||
// Base class for MIDI API handling | |||
//---------------------------------------------------------------- | |||
class midi_handler : public midi { | |||
protected: | |||
std::vector<midi*> fMidiInputs; | |||
std::string fName; | |||
public: | |||
midi_handler(const std::string& name = "MIDIHandler"):fName(name) {} | |||
virtual ~midi_handler() {} | |||
virtual void addMidiIn(midi* midi_dsp) { fMidiInputs.push_back(midi_dsp); } | |||
virtual void removeMidiIn(midi* midi_dsp) | |||
{ | |||
std::vector<midi*>::iterator it = std::find(fMidiInputs.begin(), fMidiInputs.end(), midi_dsp); | |||
if (it != fMidiInputs.end()) { | |||
fMidiInputs.erase(it); | |||
} | |||
} | |||
virtual bool start_midi() { return false; } | |||
virtual void stop_midi() {} | |||
void handleSync(double time, int type) | |||
{ | |||
if (type == MIDI_CLOCK) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->clock(time); | |||
} | |||
} else if (type == MIDI_START) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->start_sync(time); | |||
} | |||
} else if (type == MIDI_STOP) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->stop_sync(time); | |||
} | |||
} | |||
} | |||
void handleData1(double time, int type, int channel, int data1) | |||
{ | |||
if (type == MIDI_PROGRAM_CHANGE) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->progChange(time, channel, data1); | |||
} | |||
} else if (type == MIDI_AFTERTOUCH) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->chanPress(time, channel, data1); | |||
} | |||
} | |||
} | |||
void handleData2(double time, int type, int channel, int data1, int data2) | |||
{ | |||
if (type == MIDI_NOTE_OFF || ((type == MIDI_NOTE_ON) && (data2 == 0))) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->keyOff(time, channel, data1, data2); | |||
} | |||
} else if (type == MIDI_NOTE_ON) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->keyOn(time, channel, data1, data2); | |||
} | |||
} else if (type == MIDI_CONTROL_CHANGE) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->ctrlChange(time, channel, data1, data2); | |||
} | |||
} else if (type == MIDI_PITCH_BEND) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->pitchWheel(time, channel, (data2 * 128.0) + data1); | |||
} | |||
} else if (type == MIDI_POLY_AFTERTOUCH) { | |||
for (unsigned int i = 0; i < fMidiInputs.size(); i++) { | |||
fMidiInputs[i]->keyPress(time, channel, data1, data2); | |||
} | |||
} | |||
} | |||
}; | |||
#endif // __midi__ |
@@ -0,0 +1,260 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __rt_midi__ | |||
#define __rt_midi__ | |||
#include <iostream> | |||
#include <cstdlib> | |||
#include "faust/midi/RtMidi.h" | |||
#include "faust/midi/midi.h" | |||
class MapUI; | |||
class rt_midi : public midi_handler { | |||
private: | |||
std::vector<RtMidiIn*> fInput; | |||
std::vector<RtMidiOut*> fOutput; | |||
static void midiCallback(double time, std::vector<unsigned char>* message, void* arg) | |||
{ | |||
rt_midi* midi = static_cast<rt_midi*>(arg); | |||
unsigned int nBytes = message->size(); | |||
int type = (int)message->at(0) & 0xf0; | |||
int channel = (int)message->at(0) & 0x0f; | |||
// MIDI sync | |||
if (nBytes == 1) { | |||
midi->handleSync(time, (int)message->at(0)); | |||
} else if (nBytes == 2) { | |||
midi->handleData1(time, type, channel, (int)message->at(1)); | |||
} else if (nBytes == 3) { | |||
midi->handleData2(time, type, channel, (int)message->at(1), (int)message->at(2)); | |||
} | |||
} | |||
bool openMidiInputPorts() | |||
{ | |||
// Get number of input ports | |||
RtMidiIn midi_in; | |||
unsigned nInPorts = midi_in.getPortCount(); | |||
if (nInPorts == 0) { | |||
std::cout << "No input ports available!" << std::endl; | |||
return false; | |||
} | |||
// Then open all of them | |||
for (unsigned int i = 0; i < nInPorts; i++) { | |||
RtMidiIn* midi_in = new RtMidiIn(); | |||
midi_in->ignoreTypes(true, false, true); | |||
fInput.push_back(midi_in); | |||
midi_in->openPort(i); | |||
midi_in->setCallback(&midiCallback, this); | |||
std::string portName = midi_in->getPortName(i); | |||
std::cout << "Input port #" << i << ": " << portName << '\n'; | |||
} | |||
return true; | |||
} | |||
bool openMidiOutputPorts() | |||
{ | |||
// Get number of output ports | |||
RtMidiOut midi_out; | |||
unsigned nOutPorts = midi_out.getPortCount(); | |||
if (nOutPorts == 0) { | |||
std::cout << "No output ports available!" << std::endl; | |||
return false; | |||
} | |||
// Then open all of them | |||
for (unsigned int i = 0; i < nOutPorts; i++) { | |||
RtMidiOut* midi_out = new RtMidiOut(); | |||
fOutput.push_back(midi_out); | |||
midi_out->openPort(i); | |||
std::string portName = midi_out->getPortName(i); | |||
std::cout << "Output port #" << i << ": " << portName << '\n'; | |||
} | |||
return true; | |||
} | |||
void chooseMidiInputPort(const std::string& name) | |||
{ | |||
RtMidiIn* midi_in = new RtMidiIn(); | |||
midi_in->ignoreTypes(true, false, true); | |||
fInput.push_back(midi_in); | |||
midi_in->setCallback(&midiCallback, this); | |||
midi_in->openVirtualPort(name); | |||
} | |||
void chooseMidiOutPort(const std::string& name) | |||
{ | |||
RtMidiOut* midi_out = new RtMidiOut(); | |||
fOutput.push_back(midi_out); | |||
midi_out->openVirtualPort(name); | |||
} | |||
void sendMessage(std::vector<unsigned char>& message) | |||
{ | |||
std::vector<RtMidiOut*>::iterator it; | |||
for (it = fOutput.begin(); it != fOutput.end(); it++) { | |||
(*it)->sendMessage(&message); | |||
} | |||
} | |||
public: | |||
rt_midi(const std::string& name = "RtMidi"):midi_handler(name) | |||
{} | |||
virtual ~rt_midi() | |||
{ | |||
stop_midi(); | |||
} | |||
bool start_midi() | |||
{ | |||
try { | |||
#if TARGET_OS_IPHONE | |||
if (!openMidiInputPorts()) { stop_midi(); return false; } | |||
if (!openMidiOutputPorts()) { stop_midi(); return false; } | |||
#else | |||
chooseMidiInputPort(fName); | |||
chooseMidiOutPort(fName); | |||
#endif | |||
return true; | |||
} catch (RtMidiError &error) { | |||
error.printMessage(); | |||
stop_midi(); | |||
return false; | |||
} | |||
} | |||
void stop_midi() | |||
{ | |||
std::vector<RtMidiIn*>::iterator it1; | |||
for (it1 = fInput.begin(); it1 != fInput.end(); it1++) { | |||
delete (*it1); | |||
} | |||
fInput.clear(); | |||
std::vector<RtMidiOut*>::iterator it2; | |||
for (it2 = fOutput.begin(); it2 != fOutput.end(); it2++) { | |||
delete (*it2); | |||
} | |||
fOutput.clear(); | |||
} | |||
MapUI* keyOn(int channel, int pitch, int velocity) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_NOTE_ON + channel); | |||
message.push_back(pitch); | |||
message.push_back(velocity); | |||
sendMessage(message); | |||
return 0; | |||
} | |||
void keyOff(int channel, int pitch, int velocity) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_NOTE_OFF + channel); | |||
message.push_back(pitch); | |||
message.push_back(velocity); | |||
sendMessage(message); | |||
} | |||
void ctrlChange(int channel, int ctrl, int val) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_CONTROL_CHANGE + channel); | |||
message.push_back(ctrl); | |||
message.push_back(val); | |||
sendMessage(message); | |||
} | |||
void chanPress(int channel, int press) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_AFTERTOUCH + channel); | |||
message.push_back(press); | |||
sendMessage(message); | |||
} | |||
void progChange(int channel, int pgm) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_PROGRAM_CHANGE + channel); | |||
message.push_back(pgm); | |||
sendMessage(message); | |||
} | |||
void keyPress(int channel, int pitch, int press) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_POLY_AFTERTOUCH + channel); | |||
message.push_back(pitch); | |||
message.push_back(press); | |||
sendMessage(message); | |||
} | |||
void pitchWheel(int channel, int wheel) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_PITCH_BEND + channel); | |||
message.push_back(wheel & 0x7F); // lsb 7bit | |||
message.push_back((wheel >> 7) & 0x7F); // msb 7bit | |||
sendMessage(message); | |||
} | |||
void ctrlChange14bits(int channel, int ctrl, int value) {} | |||
void start_sync(double date) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_START); | |||
sendMessage(message); | |||
} | |||
void stop_sync(double date) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_STOP); | |||
sendMessage(message); | |||
} | |||
void clock(double date) | |||
{ | |||
std::vector<unsigned char> message; | |||
message.push_back(MIDI_CLOCK); | |||
sendMessage(message); | |||
} | |||
}; | |||
#endif // __rt_midi__ |
@@ -0,0 +1,71 @@ | |||
/************************************************************************ | |||
************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __misc__ | |||
#define __misc__ | |||
#include <algorithm> | |||
#include <map> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include "faust/gui/meta.h" | |||
using std::max; | |||
using std::min; | |||
struct XXXX_Meta : std::map<const char*, const char*> | |||
{ | |||
void declare(const char* key, const char* value) { (*this)[key]=value; } | |||
}; | |||
struct MY_Meta : Meta, std::map<const char*, const char*> | |||
{ | |||
void declare(const char* key, const char* value) { (*this)[key]=value; } | |||
}; | |||
inline int lsr(int x, int n) { return int(((unsigned int)x) >> n); } | |||
inline int int2pow2(int x) { int r = 0; while ((1<<r) < x) r++; return r; } | |||
inline long lopt(char* argv[], const char* name, long def) | |||
{ | |||
int i; | |||
for (i = 0; argv[i]; i++) if (!strcmp(argv[i], name)) return atoi(argv[i+1]); | |||
return def; | |||
} | |||
inline bool isopt(char* argv[], const char* name) | |||
{ | |||
int i; | |||
for (i = 0; argv[i]; i++) if (!strcmp(argv[i], name)) return true; | |||
return false; | |||
} | |||
inline const char* lopts(char* argv[], const char* name, const char* def) | |||
{ | |||
int i; | |||
for (i = 0; argv[i]; i++) if (!strcmp(argv[i], name)) return argv[i+1]; | |||
return def; | |||
} | |||
#endif | |||
@@ -0,0 +1,107 @@ | |||
/* | |||
Copyright (C) 2011 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __FaustFactory__ | |||
#define __FaustFactory__ | |||
#include <stack> | |||
#include <string> | |||
#include <sstream> | |||
#include "faust/osc/FaustNode.h" | |||
#include "faust/osc/smartpointer.h" | |||
class GUI; | |||
namespace oscfaust | |||
{ | |||
class OSCIO; | |||
class RootNode; | |||
typedef class SMARTP<RootNode> SRootNode; | |||
class MessageDriven; | |||
typedef class SMARTP<MessageDriven> SMessageDriven; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief a factory to build a OSC UI hierarchy | |||
Actually, makes use of a stack to build the UI hierarchy. | |||
It includes a pointer to a OSCIO controler, but just to give it to the root node. | |||
*/ | |||
class FaustFactory | |||
{ | |||
std::stack<SMessageDriven> fNodes; ///< maintains the current hierarchy level | |||
SRootNode fRoot; ///< keep track of the root node | |||
OSCIO* fIO; ///< hack to support audio IO via OSC, actually the field is given to the root node | |||
GUI* fGUI; ///< a GUI pointer to support updateAllGuis(), required for bi-directionnal OSC | |||
private: | |||
std::string addressFirst(const std::string& address) const; | |||
std::string addressTail(const std::string& address) const; | |||
public: | |||
FaustFactory(GUI* ui, OSCIO * io = 0); | |||
virtual ~FaustFactory(); | |||
template <typename C> void addnode(const char* label, C* zone, C init, C min, C max, bool initZone, bool input); | |||
template <typename C> void addAlias(const std::string& fullpath, C* zone, C imin, C imax, C init, C min, C max, const char* label); | |||
void addAlias(const char* alias, const char* address, float imin, float imax, float omin, float omax); | |||
void opengroup(const char* label); | |||
void closegroup(); | |||
SRootNode root() const; | |||
}; | |||
/** | |||
* Add a node to the OSC UI tree in the current group at the top of the stack | |||
*/ | |||
template <typename C> void FaustFactory::addnode(const char* label, C* zone, C init, C min, C max, bool initZone, bool input) | |||
{ | |||
SMessageDriven top; | |||
if (fNodes.size()) top = fNodes.top(); | |||
if (top) { | |||
std::string prefix = top->getOSCAddress(); | |||
top->add(FaustNode<C>::create(root(), label, zone, init, min, max, prefix.c_str(), fGUI, initZone, input)); | |||
} | |||
} | |||
/** | |||
* Add an alias (actually stored and handled at root node level | |||
*/ | |||
template <typename C> void FaustFactory::addAlias(const std::string& fullpath, C* zone, C imin, C imax, C init, C min, C max, const char* label) | |||
{ | |||
std::istringstream ss(fullpath); | |||
std::string realpath; | |||
ss >> realpath >> imin >> imax; | |||
SMessageDriven top = fNodes.top(); | |||
if (top) { | |||
std::string target = top->getOSCAddress() + "/" + label; | |||
addAlias(realpath.c_str(), target.c_str(), float(imin), float(imax), float(min), float(max)); | |||
} | |||
} | |||
} // end namespoace | |||
#endif |
@@ -0,0 +1,96 @@ | |||
/* | |||
Copyright (C) 2011 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __FaustNode__ | |||
#define __FaustNode__ | |||
#include <string> | |||
#include <vector> | |||
#include "faust/osc/MessageDriven.h" | |||
#include "faust/osc/Message.h" | |||
#include "faust/gui/GUI.h" | |||
#include "faust/osc/smartpointer.h" | |||
#include "faust/osc/RootNode.h" | |||
class GUI; | |||
namespace oscfaust | |||
{ | |||
/** | |||
* map (rescale) input values to output values | |||
*/ | |||
template <typename C> struct mapping | |||
{ | |||
const C fMinOut; | |||
const C fMaxOut; | |||
mapping(C omin, C omax) : fMinOut(omin), fMaxOut(omax) {} | |||
C clip (C x) { return (x < fMinOut) ? fMinOut : (x > fMaxOut) ? fMaxOut : x; } | |||
}; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief a faust node is a terminal node and represents a faust parameter controler | |||
*/ | |||
template <typename C> class FaustNode : public MessageDriven, public uiItem | |||
{ | |||
mapping<C> fMapping; | |||
RootNode* fRoot; | |||
bool fInput; // true for input nodes (slider, button...) | |||
bool store(C val) { *fZone = fMapping.clip(val); return true; } | |||
void sendOSC() const; | |||
protected: | |||
FaustNode(RootNode* root, const char *name, C* zone, C init, C min, C max, const char* prefix, GUI* ui, bool initZone, bool input) | |||
: MessageDriven(name, prefix), uiItem(ui, zone), fRoot(root), fMapping(min, max), fInput(input) | |||
{ | |||
if (initZone) { | |||
*zone = init; | |||
} | |||
} | |||
virtual ~FaustNode() {} | |||
public: | |||
typedef SMARTP<FaustNode<C> > SFaustNode; | |||
static SFaustNode create(RootNode* root, const char* name, C* zone, C init, C min, C max, const char* prefix, GUI* ui, bool initZone, bool input) | |||
{ | |||
SFaustNode node = new FaustNode(root, name, zone, init, min, max, prefix, ui, initZone, input); | |||
/* | |||
Since FaustNode is a subclass of uiItem, the pointer will also be kept in the GUI class, and it's desallocation will be done there. | |||
So we don't want to have smartpointer logic desallocate it and we increment the refcount. | |||
*/ | |||
node->addReference(); | |||
return node; | |||
} | |||
bool accept(const Message* msg); | |||
void get(unsigned long ipdest) const; ///< handler for the 'get' message | |||
virtual void reflectZone() { sendOSC(); fCache = *fZone; } | |||
}; | |||
} // end namespace | |||
#endif |
@@ -0,0 +1,263 @@ | |||
/* | |||
Copyright (C) 2011 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __Message__ | |||
#define __Message__ | |||
#include <string> | |||
#include <vector> | |||
#include "faust/osc/smartpointer.h" | |||
namespace oscfaust | |||
{ | |||
class OSCStream; | |||
template <typename T> class MsgParam; | |||
class baseparam; | |||
typedef SMARTP<baseparam> Sbaseparam; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief base class of a message parameters | |||
*/ | |||
class baseparam : public smartable | |||
{ | |||
public: | |||
virtual ~baseparam() {} | |||
/*! | |||
\brief utility for parameter type checking | |||
*/ | |||
template<typename X> bool isType() const { return dynamic_cast<const MsgParam<X>*> (this) != 0; } | |||
/*! | |||
\brief utility for parameter convertion | |||
\param errvalue the returned value when no conversion applies | |||
\return the parameter value when the type matches | |||
*/ | |||
template<typename X> X value(X errvalue) const | |||
{ const MsgParam<X>* o = dynamic_cast<const MsgParam<X>*> (this); return o ? o->getValue() : errvalue; } | |||
/*! | |||
\brief utility for parameter comparison | |||
*/ | |||
template<typename X> bool equal(const baseparam& p) const | |||
{ | |||
const MsgParam<X>* a = dynamic_cast<const MsgParam<X>*> (this); | |||
const MsgParam<X>* b = dynamic_cast<const MsgParam<X>*> (&p); | |||
return a && b && (a->getValue() == b->getValue()); | |||
} | |||
/*! | |||
\brief utility for parameter comparison | |||
*/ | |||
bool operator==(const baseparam& p) const | |||
{ | |||
return equal<float>(p) || equal<int>(p) || equal<std::string>(p); | |||
} | |||
bool operator!=(const baseparam& p) const | |||
{ | |||
return !equal<float>(p) && !equal<int>(p) && !equal<std::string>(p); | |||
} | |||
virtual SMARTP<baseparam> copy() const = 0; | |||
}; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief template for a message parameter | |||
*/ | |||
template <typename T> class MsgParam : public baseparam | |||
{ | |||
T fParam; | |||
public: | |||
MsgParam(T val) : fParam(val) {} | |||
virtual ~MsgParam() {} | |||
T getValue() const { return fParam; } | |||
virtual Sbaseparam copy() const { return new MsgParam<T>(fParam); } | |||
}; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief a message description | |||
A message is composed of an address (actually an OSC address), | |||
a message string that may be viewed as a method name | |||
and a list of message parameters. | |||
*/ | |||
class Message | |||
{ | |||
public: | |||
typedef SMARTP<baseparam> argPtr; ///< a message argument ptr type | |||
typedef std::vector<argPtr> argslist; ///< args list type | |||
private: | |||
unsigned long fSrcIP; ///< the message source IP number | |||
std::string fAddress; ///< the message osc destination address | |||
std::string fAlias; ///< the message alias osc destination address | |||
argslist fArguments; ///< the message arguments | |||
public: | |||
/*! | |||
\brief an empty message constructor | |||
*/ | |||
Message() {} | |||
/*! | |||
\brief a message constructor | |||
\param address the message destination address | |||
*/ | |||
Message(const std::string& address) : fAddress(address), fAlias("") {} | |||
Message(const std::string& address, const std::string& alias) : fAddress(address), fAlias(alias) {} | |||
/*! | |||
\brief a message constructor | |||
\param address the message destination address | |||
\param args the message parameters | |||
*/ | |||
Message(const std::string& address, const argslist& args) | |||
: fAddress(address), fArguments(args) {} | |||
/*! | |||
\brief a message constructor | |||
\param msg a message | |||
*/ | |||
Message(const Message& msg); | |||
virtual ~Message() {} //{ freed++; std::cout << "running messages: " << (allocated - freed) << std::endl; } | |||
/*! | |||
\brief adds a parameter to the message | |||
\param val the parameter | |||
*/ | |||
template <typename T> void add(T val) { fArguments.push_back(new MsgParam<T>(val)); } | |||
/*! | |||
\brief adds a float parameter to the message | |||
\param val the parameter value | |||
*/ | |||
void add(float val) { add<float>(val); } | |||
/*! | |||
\brief adds an int parameter to the message | |||
\param val the parameter value | |||
*/ | |||
void add(int val) { add<int>(val); } | |||
/*! | |||
\brief adds a string parameter to the message | |||
\param val the parameter value | |||
*/ | |||
void add(const std::string& val) { add<std::string>(val); } | |||
/*! | |||
\brief adds a parameter to the message | |||
\param val the parameter | |||
*/ | |||
void add(argPtr val) { fArguments.push_back( val ); } | |||
/*! | |||
\brief sets the message address | |||
\param addr the address | |||
*/ | |||
void setSrcIP(unsigned long addr) { fSrcIP = addr; } | |||
/*! | |||
\brief sets the message address | |||
\param addr the address | |||
*/ | |||
void setAddress(const std::string& addr) { fAddress = addr; } | |||
/*! | |||
\brief print the message | |||
\param out the output stream | |||
*/ | |||
void print(std::ostream& out) const; | |||
/*! | |||
\brief send the message to OSC | |||
\param out the OSC output stream | |||
*/ | |||
void print(OSCStream& out) const; | |||
/*! | |||
\brief print message arguments | |||
\param out the OSC output stream | |||
*/ | |||
void printArgs(OSCStream& out) const; | |||
/// \brief gives the message address | |||
const std::string& address() const { return fAddress; } | |||
/// \brief gives the message alias | |||
const std::string& alias() const { return fAlias; } | |||
/// \brief gives the message parameters list | |||
const argslist& params() const { return fArguments; } | |||
/// \brief gives the message parameters list | |||
argslist& params() { return fArguments; } | |||
/// \brief gives the message source IP | |||
unsigned long src() const { return fSrcIP; } | |||
/// \brief gives the message parameters count | |||
int size() const { return fArguments.size(); } | |||
bool operator == (const Message& other) const; | |||
/*! | |||
\brief gives a message float parameter | |||
\param i the parameter index (0 <= i < size()) | |||
\param val on output: the parameter value when the parameter type matches | |||
\return false when types don't match | |||
*/ | |||
bool param(int i, float& val) const { val = params()[i]->value<float>(val); return params()[i]->isType<float>(); } | |||
/*! | |||
\brief gives a message int parameter | |||
\param i the parameter index (0 <= i < size()) | |||
\param val on output: the parameter value when the parameter type matches | |||
\return false when types don't match | |||
*/ | |||
bool param(int i, int& val) const { val = params()[i]->value<int>(val); return params()[i]->isType<int>(); } | |||
/*! | |||
\brief gives a message int parameter | |||
\param i the parameter index (0 <= i < size()) | |||
\param val on output: the parameter value when the parameter type matches | |||
\return false when types don't match | |||
*/ | |||
bool param(int i, unsigned int& val) const { val = params()[i]->value<int>(val); return params()[i]->isType<int>(); } | |||
/*! | |||
\brief gives a message int parameter | |||
\param i the parameter index (0 <= i < size()) | |||
\param val on output: the parameter value when the parameter type matches | |||
\return false when types don't match | |||
\note a boolean value is handled as integer | |||
*/ | |||
bool param(int i, bool& val) const { int ival = 0; ival = params()[i]->value<int>(ival); val = ival!=0; return params()[i]->isType<int>(); } | |||
/*! | |||
\brief gives a message int parameter | |||
\param i the parameter index (0 <= i < size()) | |||
\param val on output: the parameter value when the parameter type matches | |||
\return false when types don't match | |||
*/ | |||
bool param(int i, long int& val) const { val = long(params()[i]->value<int>(val)); return params()[i]->isType<int>(); } | |||
/*! | |||
\brief gives a message string parameter | |||
\param i the parameter index (0 <= i < size()) | |||
\param val on output: the parameter value when the parameter type matches | |||
\return false when types don't match | |||
*/ | |||
bool param(int i, std::string& val) const { val = params()[i]->value<std::string>(val); return params()[i]->isType<std::string>(); } | |||
}; | |||
} // end namespoace | |||
#endif |
@@ -0,0 +1,131 @@ | |||
/* | |||
Copyright (C) 2011 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __MessageDriven__ | |||
#define __MessageDriven__ | |||
#include <string> | |||
#include <vector> | |||
#include "faust/osc/MessageProcessor.h" | |||
#include "faust/osc/smartpointer.h" | |||
namespace oscfaust | |||
{ | |||
class Message; | |||
class OSCRegexp; | |||
class MessageDriven; | |||
typedef class SMARTP<MessageDriven> SMessageDriven; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief a base class for objects accepting OSC messages | |||
Message driven objects are hierarchically organized in a tree. | |||
They provides the necessary to dispatch an OSC message to its destination | |||
node, according to the message OSC address. | |||
The principle of the dispatch is the following: | |||
- first the processMessage() method should be called on the top level node | |||
- next processMessage call propose | |||
*/ | |||
class MessageDriven : public MessageProcessor, public smartable | |||
{ | |||
std::string fName; ///< the node name | |||
std::string fOSCPrefix; ///< the node OSC address prefix (OSCAddress = fOSCPrefix + '/' + fName) | |||
std::vector<SMessageDriven> fSubNodes; ///< the subnodes of the current node | |||
protected: | |||
MessageDriven(const char *name, const char *oscprefix) : fName (name), fOSCPrefix(oscprefix) {} | |||
virtual ~MessageDriven() {} | |||
public: | |||
static SMessageDriven create(const char* name, const char *oscprefix) { return new MessageDriven(name, oscprefix); } | |||
/*! | |||
\brief OSC message processing method. | |||
\param msg the osc message to be processed | |||
The method should be called on the top level node. | |||
*/ | |||
virtual void processMessage(const Message* msg); | |||
/*! | |||
\brief propose an OSc message at a given hierarchy level. | |||
\param msg the osc message currently processed | |||
\param regexp a regular expression based on the osc address head | |||
\param addrTail the osc address tail | |||
The method first tries to match the regular expression with the object name. | |||
When it matches: | |||
- it calls \c accept when \c addrTail is empty | |||
- or it \c propose the message to its subnodes when \c addrTail is not empty. | |||
In this case a new \c regexp is computed with the head of \c addrTail and a new \c addrTail as well. | |||
*/ | |||
virtual void propose(const Message* msg, const OSCRegexp* regexp, const std::string addrTail); | |||
/*! | |||
\brief accept an OSC message. | |||
\param msg the osc message currently processed | |||
\return true when the message is processed by the node | |||
The method is called only for the destination nodes. The real message acceptance is the node | |||
responsability and may depend on the message content. | |||
*/ | |||
virtual bool accept(const Message* msg); | |||
/*! | |||
\brief handler for the \c 'get' message | |||
\param ipdest the output message destination IP | |||
The \c 'get' message is supported by every node: | |||
- it is propagated to the subnodes until it reaches terminal nodes | |||
- a terminal node send its state on \c 'get' request to the IP address given as parameter. | |||
The \c get method is basically called by the accept method. | |||
*/ | |||
virtual void get(unsigned long ipdest) const; | |||
/*! | |||
\brief handler for the \c 'get' 'attribute' message | |||
\param ipdest the output message destination IP | |||
\param what the requested attribute | |||
The \c 'get' message is supported by every node: | |||
- it is propagated to the subnodes until it reaches terminal nodes | |||
- a terminal node send its state on \c 'get' request to the IP address given as parameter. | |||
The \c get method is basically called by the accept method. | |||
*/ | |||
virtual void get (unsigned long ipdest, const std::string & what) const {} | |||
void add(SMessageDriven node) { fSubNodes.push_back (node); } | |||
const char* getName() const { return fName.c_str(); } | |||
std::string getOSCAddress() const; | |||
int size() const { return fSubNodes.size (); } | |||
const std::string& name() const { return fName; } | |||
SMessageDriven subnode(int i) { return fSubNodes[i]; } | |||
}; | |||
} // end namespoace | |||
#endif |
@@ -0,0 +1,44 @@ | |||
/* | |||
Copyright (C) 2010 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __MessageProcessor__ | |||
#define __MessageProcessor__ | |||
namespace oscfaust | |||
{ | |||
class Message; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief an abstract class for objects able to process OSC messages | |||
*/ | |||
class MessageProcessor | |||
{ | |||
public: | |||
virtual ~MessageProcessor() {} | |||
virtual void processMessage( const Message* msg ) = 0; | |||
}; | |||
} // end namespoace | |||
#endif |
@@ -0,0 +1,115 @@ | |||
/* | |||
Copyright (C) 2011 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __RootNode__ | |||
#define __RootNode__ | |||
#include <map> | |||
#include <string> | |||
#include <vector> | |||
#include "faust/osc/MessageDriven.h" | |||
namespace oscfaust | |||
{ | |||
class OSCIO; | |||
class RootNode; | |||
typedef class SMARTP<RootNode> SRootNode; | |||
/** | |||
* an alias target includes a map to rescale input values to output values | |||
* and a target osc address. The input values can be given in reversed order | |||
* to reverse the control | |||
*/ | |||
struct aliastarget | |||
{ | |||
float fMinIn; | |||
float fMaxIn; | |||
float fMinOut; | |||
float fMaxOut; | |||
std::string fTarget; // the real osc address | |||
aliastarget(const char* address, float imin, float imax, float omin, float omax) | |||
: fMinIn(imin), fMaxIn(imax), fMinOut(omin), fMaxOut(omax), fTarget(address) {} | |||
aliastarget(const aliastarget& t) | |||
: fMinIn(t.fMinIn), fMaxIn(t.fMaxIn), fMinOut(t.fMinOut), fMaxOut(t.fMaxOut), fTarget(t.fTarget) {} | |||
float scale(float x) const | |||
{ | |||
if (fMinIn < fMaxIn) { | |||
// increasing control | |||
float z = (x < fMinIn) ? fMinIn : (x > fMaxIn) ? fMaxIn : x; | |||
return fMinOut + (z-fMinIn)*(fMaxOut-fMinOut)/(fMaxIn-fMinIn); | |||
} else if (fMinIn > fMaxIn) { | |||
// reversed control | |||
float z = (x < fMaxIn) ? fMaxIn : (x > fMinIn) ? fMinIn : x; | |||
return fMinOut + (fMinIn-z)*(fMaxOut-fMinOut)/(fMinIn-fMaxIn); | |||
} else { | |||
// no control ! | |||
return (fMinOut+fMaxOut)/2.0; | |||
} | |||
} | |||
}; | |||
//-------------------------------------------------------------------------- | |||
/*! | |||
\brief a faust root node | |||
A Faust root node handles the \c 'hello' message and provides support | |||
for incoming osc signal data. | |||
*/ | |||
class RootNode : public MessageDriven | |||
{ | |||
int *fUPDIn, *fUDPOut, *fUDPErr; // the osc port numbers (required by the hello method) | |||
OSCIO* fIO; // an OSC IO controler | |||
std::map<std::string, std::vector<aliastarget> > fAliases; | |||
void processAlias(const std::string& address, float val); | |||
protected: | |||
RootNode(const char *name, OSCIO* io = 0) : MessageDriven(name, ""), fUPDIn(0), fUDPOut(0), fUDPErr(0), fIO(io) {} | |||
virtual ~RootNode() {} | |||
public: | |||
static SRootNode create(const char* name, OSCIO* io = 0) { return new RootNode(name, io); } | |||
virtual void processMessage(const Message* msg); | |||
virtual bool accept(const Message* msg); | |||
virtual void get(unsigned long ipdest) const; | |||
virtual void get(unsigned long ipdest, const std::string& what) const; | |||
void addAlias(const char* alias, const char* address, float imin, float imax, float omin, float omax); | |||
bool acceptSignal(const Message* msg); ///< handler for signal data | |||
void hello(unsigned long ipdest) const; ///< handler for the 'hello' message | |||
void setPorts(int* in, int* out, int* err); | |||
std::vector<std::string> getAliases(const std::string& address); | |||
}; | |||
} // end namespoace | |||
#endif |
@@ -0,0 +1,139 @@ | |||
/* | |||
Copyright (C) 2011 Grame | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library 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 | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Grame Research Laboratory, 9 rue du Garet, 69001 Lyon - France | |||
research@grame.fr | |||
*/ | |||
#ifndef __smartpointer__ | |||
#define __smartpointer__ | |||
#include <cassert> | |||
namespace oscfaust | |||
{ | |||
/*! | |||
\brief the base class for smart pointers implementation | |||
Any object that want to support smart pointers should | |||
inherit from the smartable class which provides reference counting | |||
and automatic delete when the reference count drops to zero. | |||
*/ | |||
class smartable { | |||
private: | |||
unsigned refCount; | |||
public: | |||
//! gives the reference count of the object | |||
unsigned refs() const { return refCount; } | |||
//! addReference increments the ref count and checks for refCount overflow | |||
void addReference() { refCount++; assert(refCount != 0); } | |||
//! removeReference delete the object when refCount is zero | |||
void removeReference() { if (--refCount == 0) delete this; } | |||
protected: | |||
smartable() : refCount(0) {} | |||
smartable(const smartable&): refCount(0) {} | |||
//! destructor checks for non-zero refCount | |||
virtual ~smartable() | |||
{ | |||
/* | |||
See "Static SFaustNode create (const char* name, C* zone, C init, C min, C max, const char* prefix, GUI* ui)" comment. | |||
assert (refCount == 0); | |||
*/ | |||
} | |||
smartable& operator=(const smartable&) { return *this; } | |||
}; | |||
/*! | |||
\brief the smart pointer implementation | |||
A smart pointer is in charge of maintaining the objects reference count | |||
by the way of pointers operators overloading. It supports class | |||
inheritance and conversion whenever possible. | |||
\n Instances of the SMARTP class are supposed to use \e smartable types (or at least | |||
objects that implements the \e addReference and \e removeReference | |||
methods in a consistent way). | |||
*/ | |||
template<class T> class SMARTP { | |||
private: | |||
//! the actual pointer to the class | |||
T* fSmartPtr; | |||
public: | |||
//! an empty constructor - points to null | |||
SMARTP() : fSmartPtr(0) {} | |||
//! build a smart pointer from a class pointer | |||
SMARTP(T* rawptr) : fSmartPtr(rawptr) { if (fSmartPtr) fSmartPtr->addReference(); } | |||
//! build a smart pointer from an convertible class reference | |||
template<class T2> | |||
SMARTP(const SMARTP<T2>& ptr) : fSmartPtr((T*)ptr) { if (fSmartPtr) fSmartPtr->addReference(); } | |||
//! build a smart pointer from another smart pointer reference | |||
SMARTP(const SMARTP& ptr) : fSmartPtr((T*)ptr) { if (fSmartPtr) fSmartPtr->addReference(); } | |||
//! the smart pointer destructor: simply removes one reference count | |||
~SMARTP() { if (fSmartPtr) fSmartPtr->removeReference(); } | |||
//! cast operator to retrieve the actual class pointer | |||
operator T*() const { return fSmartPtr; } | |||
//! '*' operator to access the actual class pointer | |||
T& operator*() const { | |||
// checks for null dereference | |||
assert (fSmartPtr != 0); | |||
return *fSmartPtr; | |||
} | |||
//! operator -> overloading to access the actual class pointer | |||
T* operator->() const { | |||
// checks for null dereference | |||
assert (fSmartPtr != 0); | |||
return fSmartPtr; | |||
} | |||
//! operator = that moves the actual class pointer | |||
template <class T2> | |||
SMARTP& operator=(T2 p1_) { *this=(T*)p1_; return *this; } | |||
//! operator = that moves the actual class pointer | |||
SMARTP& operator=(T* p_) { | |||
// check first that pointers differ | |||
if (fSmartPtr != p_) { | |||
// increments the ref count of the new pointer if not null | |||
if (p_ != 0) p_->addReference(); | |||
// decrements the ref count of the old pointer if not null | |||
if (fSmartPtr != 0) fSmartPtr->removeReference(); | |||
// and finally stores the new actual pointer | |||
fSmartPtr = p_; | |||
} | |||
return *this; | |||
} | |||
//! operator < to support SMARTP map with Visual C++ | |||
bool operator<(const SMARTP<T>& p_) const { return fSmartPtr < ((T *) p_); } | |||
//! operator = to support inherited class reference | |||
SMARTP& operator=(const SMARTP<T>& p_) { return operator=((T *) p_); } | |||
//! dynamic cast support | |||
template<class T2> SMARTP& cast(T2* p_) { return operator=(dynamic_cast<T*>(p_)); } | |||
//! dynamic cast support | |||
template<class T2> SMARTP& cast(const SMARTP<T2>& p_) { return operator=(dynamic_cast<T*>(p_)); } | |||
}; | |||
} | |||
#endif |
@@ -0,0 +1,133 @@ | |||
/************************************************************************ | |||
FAUST Architecture File | |||
Copyright (C) 2003-2016 GRAME, Centre National de Creation Musicale | |||
--------------------------------------------------------------------- | |||
This Architecture section 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/>. | |||
EXCEPTION : As a special exception, you may create a larger work | |||
that contains this FAUST architecture section and distribute | |||
that work under terms of your choice, so long as this FAUST | |||
architecture section is not modified. | |||
************************************************************************ | |||
************************************************************************/ | |||
#ifndef __SoundFileReader__ | |||
#define __SoundFileReader__ | |||
#include <sndfile.h> | |||
#include <stdlib.h> | |||
#define BUFFER_SIZE 1024 | |||
#ifndef FAUSTFLOAT | |||
#define READ_SAMPLE sf_readf_float | |||
#define FAUSTFLOAT float | |||
#else | |||
#define READ_SAMPLE sf_readf_double | |||
#define FAUSTFLOAT double | |||
#endif | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
typedef struct SoundFileReader { | |||
FAUSTFLOAT** fBuffer; | |||
SNDFILE* fSoundFile; | |||
int fChannels; | |||
int fFramesNum; | |||
} SoundFileReader; | |||
inline static SoundFileReader* createSFR(const char* name) | |||
{ | |||
SoundFileReader* reader = (SoundFileReader*)calloc(1, sizeof(SoundFileReader)); | |||
if (!reader) return NULL; | |||
{ | |||
SF_INFO snd_info; | |||
snd_info.format = 0; | |||
reader->fSoundFile = sf_open(name, SFM_READ, &snd_info); | |||
if (!reader->fSoundFile) { | |||
printf("soundfile '%s' cannot be opened\n", name); | |||
goto error; | |||
} | |||
reader->fChannels = snd_info.channels; | |||
reader->fFramesNum = snd_info.frames; | |||
reader->fBuffer = (FAUSTFLOAT**)malloc(reader->fChannels * sizeof(FAUSTFLOAT*)); | |||
if (!reader) goto error; | |||
for (int i = 0; i < reader->fChannels; i++) { | |||
reader->fBuffer[i] = (FAUSTFLOAT*)malloc(reader->fFramesNum * sizeof(FAUSTFLOAT)); | |||
if (!reader->fBuffer[i]) goto error; | |||
} | |||
// Read file in memory | |||
int nbf, cur_index = 0; | |||
FAUSTFLOAT buffer[BUFFER_SIZE * reader->fChannels]; | |||
do { | |||
nbf = READ_SAMPLE(reader->fSoundFile, buffer, BUFFER_SIZE); | |||
for (int sample = 0; sample < nbf; sample++) { | |||
for (int chan = 0; chan < reader->fChannels; chan++) { | |||
reader->fBuffer[chan][cur_index + sample] = buffer[sample * reader->fChannels + chan]; | |||
} | |||
} | |||
cur_index += nbf; | |||
} while (nbf == BUFFER_SIZE); | |||
return reader; | |||
} | |||
error: | |||
if (reader->fBuffer) free(reader->fBuffer); | |||
if (reader) free(reader); | |||
return NULL; | |||
} | |||
inline static void destroySFR(SoundFileReader* reader) | |||
{ | |||
if (reader) { | |||
sf_close(reader->fSoundFile); | |||
for (int i = 0; i < reader->fChannels; i++) { | |||
free(reader->fBuffer[i]); | |||
} | |||
free(reader->fBuffer); | |||
free(reader); | |||
} | |||
} | |||
inline static int sizeSFR(SoundFileReader* reader) | |||
{ | |||
return (reader) ? reader->fFramesNum : 1; | |||
} | |||
inline static int channelsSFR(SoundFileReader* reader) | |||
{ | |||
return (reader) ? reader->fChannels : 1; | |||
} | |||
inline static FAUSTFLOAT sampleSFR(SoundFileReader* reader, int channel, int index) | |||
{ | |||
return (reader) ? reader->fBuffer[channel][index] : 0.f; | |||
} | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif |
@@ -0,0 +1,116 @@ | |||
#ifndef __VST_FAUST_H__ | |||
#define __VST_FAUST_H__ | |||
#include <list> | |||
#include <map> | |||
#include <vector> | |||
#include "audioeffectx.h" | |||
//#include "../audio/dsp.h" | |||
const int MAX_POLYPHONY = 10; | |||
const int INITIAL_TEMP_OUTPUT_SIZE = 1024; | |||
////////////////////////////////////////////////////////////////// | |||
// Faust class definition | |||
// This class implements the abstract methods of AudioEffectX | |||
// that define the VST instrument behavior. | |||
////////////////////////////////////////////////////////////////// | |||
class Faust : public AudioEffectX { | |||
public: | |||
// Constructor | |||
Faust(audioMasterCallback audioMaster, dsp* dspi, vstUI* dspUIi); | |||
// Destructor | |||
virtual ~Faust(); | |||
virtual void processReplacing (FAUSTFLOAT** inputs, FAUSTFLOAT** outputs, VstInt32 sampleFrames); | |||
virtual VstInt32 processEvents (VstEvents* events); | |||
virtual void setProgram (VstInt32 program); | |||
virtual void setProgramName (const char *name); | |||
virtual void getProgramName (char *name); | |||
virtual bool getProgramNameIndexed (VstInt32 category, VstInt32 index, char *text); | |||
virtual void setParameter (VstInt32 index, float value); | |||
virtual float getParameter (VstInt32 index); | |||
virtual void getParameterLabel (VstInt32 index, char *label); | |||
virtual void getParameterDisplay (VstInt32 index, char *text); | |||
virtual void getParameterName (VstInt32 index, char *text); | |||
virtual bool getParameterProperties(VstInt32 index, VstParameterProperties* properties); | |||
virtual void setSampleRate (float sampleRate); | |||
virtual bool getInputProperties (VstInt32 index, VstPinProperties *properties); | |||
virtual bool getOutputProperties (VstInt32 index, VstPinProperties *properties); | |||
virtual bool getEffectName (char *name); | |||
virtual bool getVendorString (char *text); | |||
virtual bool getProductString (char *text); | |||
virtual VstInt32 getVendorVersion (); | |||
virtual VstInt32 canDo (const char *text); | |||
virtual VstInt32 getNumMidiInputChannels (); | |||
virtual VstInt32 getNumMidiOutputChannels (); | |||
virtual VstInt32 getMidiProgramName (VstInt32 channel, MidiProgramName *midiProgramName); | |||
virtual VstInt32 getCurrentMidiProgram (VstInt32 channel, MidiProgramName *currentProgram); | |||
virtual VstInt32 getMidiProgramCategory (VstInt32 channel, MidiProgramCategory *category); | |||
private: | |||
// Get metadata supplied by Faust compiler | |||
const char* getMetadata(const char* key, const char* defaultString); | |||
void initProcess (); | |||
void noteOn (VstInt32 note, VstInt32 velocity, VstInt32 delta); | |||
void noteOff (VstInt32 note, VstInt32 delta); | |||
void allNotesOff( void ); | |||
void fillProgram (VstInt32 channel, VstInt32 prg, MidiProgramName* mpn); | |||
void synthProcessReplacing(float **inputs, float **outputs, | |||
VstInt32 sampleFrames); | |||
void compute(float** inputs, float** outputs, VstInt32 sampleFrames); | |||
void bendPitch(float bend); | |||
dsp* m_dsp; | |||
vstUI* m_dspUI; | |||
// For synths: | |||
bool noteIsOn; | |||
VstInt32 currentNote; | |||
VstInt32 currentVelocity; | |||
VstInt32 currentDelta; | |||
std::list<VstInt32> m_currentNotes; | |||
std::list<VstInt32> m_currentVelocities; | |||
std::list<VstInt32> m_currentDeltas; | |||
char programName[kVstMaxProgNameLen + 1]; | |||
// Polyphony | |||
std::vector<Voice*> m_voices; | |||
// Occupied voices - map note to voice index | |||
struct voice_node { | |||
VstInt32 note; | |||
int voice; | |||
}; | |||
std::list<voice_node*> m_playingVoices; | |||
// key off but still sounding | |||
std :: vector< int > m_releasedVoices; | |||
// Free voices - currently rot playing | |||
std :: list< int > m_freeVoices; | |||
// Previously played voice | |||
int m_prevVoice; | |||
FAUSTFLOAT** m_tempOutputs; | |||
size_t m_tempOutputSize; // in float frames | |||
}; // end of Faust class | |||
#endif |
@@ -0,0 +1,22 @@ | |||
////////////////////////////////////////////////////// | |||
// Faust VST Voice | |||
////////////////////////////////////////////////////// | |||
#ifndef __VST_VOICE_H__ | |||
#define __VST_VOICE_H__ | |||
//#include "../audio/dsp.h" | |||
class Voice : public vstUI { | |||
public: | |||
Voice(int samplingRate) | |||
: vstUI(), m_dsp() | |||
{ | |||
m_dsp.init(samplingRate); | |||
m_dsp.buildUserInterface(this); | |||
} | |||
mydsp m_dsp; | |||
}; //end of Voice vlass | |||
#endif |
@@ -0,0 +1,455 @@ | |||
#ifndef __VST_UI_H__ | |||
#define __VST_UI_H__ | |||
#include <audioeffectx.h> | |||
#include <faust/gui/UI.h> | |||
#ifdef DEBUG | |||
#define TRACE(x) x | |||
#else | |||
#define TRACE(x) | |||
#endif | |||
//////////////////////////// VST UI //////////////////////// | |||
class vstUIObject { /* superclass of all VST UI widgets */ | |||
protected: | |||
string fLabel; | |||
float* fZone; | |||
Meta metadata; | |||
float clip(float min, float max, float val) | |||
{ | |||
return (val < min) ? min : (val > max) ? max : val; | |||
} | |||
float normalize(float min, float max, float val) | |||
{ // VST parameters are normalized to the range [0;1] on the host | |||
val = min + val * (max - min); | |||
return (val < min) ? min : (val > max) ? max : val; | |||
} | |||
public: | |||
vstUIObject(const char* label, float* zone) | |||
: fLabel(label), fZone(zone), metadata() | |||
{} | |||
virtual ~vstUIObject() | |||
{} | |||
virtual void GetName(char *text) { | |||
std::strcpy(text,fLabel.c_str()); | |||
} | |||
virtual void SetValue(double f) { | |||
*fZone = normalize(0.0f,1.0f,(float)f); | |||
} | |||
virtual void SetValueNoNormalization(double f) { | |||
*fZone = clip(0.0f,1.0f,(float)f); | |||
} | |||
virtual float GetValue() { | |||
return *fZone; | |||
} | |||
virtual float GetValueNoNormalization() { | |||
return *fZone; | |||
} | |||
virtual void GetDisplay(char *text){ | |||
std::sprintf(text,"%f",*fZone); | |||
} | |||
virtual long GetID() { | |||
/* returns the sum of all the ASCII characters contained in the | |||
parameter's label */ | |||
unsigned int i; | |||
long acc; | |||
for(i=0,acc = 0; i < fLabel.length(); i++) { | |||
acc += (fLabel.c_str())[i]; | |||
} | |||
return acc; | |||
} | |||
const float* getZone() const { | |||
return fZone; | |||
} | |||
}; // end of vstUIObject class | |||
/*-------------------------------------------------------------------------*/ | |||
class vstToggleButton : public vstUIObject { | |||
public: | |||
vstToggleButton(const char* label, float* zone) | |||
: vstUIObject(label,zone) | |||
{} | |||
virtual ~vstToggleButton() {} | |||
virtual float GetValue() { | |||
return *fZone; | |||
} | |||
virtual void SetValue(double f) { | |||
*fZone = (f>0.5f) ? 1.0f : 0.0f; | |||
} | |||
virtual void GetDisplay(char *text) { | |||
(*fZone > 0.5f) ? std::strcpy(text,"ON") : std::strcpy(text,"OFF"); | |||
} | |||
}; | |||
/*--------------------------------------------------------------------------*/ | |||
class vstCheckButton : public vstUIObject { | |||
public: | |||
vstCheckButton(const char* label, float* zone):vstUIObject(label,zone) {} | |||
virtual ~vstCheckButton() {} | |||
virtual float GetValue() { | |||
return *fZone; | |||
} | |||
virtual void SetValue(double f) {*fZone = (f>0.5f)?1.0f:0.0f;} | |||
virtual void GetDisplay(char *text) { | |||
(*fZone>0.5f)? std::strcpy(text,"ON"): std::strcpy(text,"OFF"); | |||
} | |||
}; | |||
/*--------------------------------------------------------------------------*/ | |||
class vstButton : public vstUIObject { | |||
public: | |||
vstButton(const char* label, float* zone) | |||
: vstUIObject(label,zone) | |||
{} | |||
virtual ~vstButton() {} | |||
virtual float GetValue() {return *fZone;} | |||
virtual void SetValue(double f) {*fZone = (f>0.5f)?1.0f:0.0f;} | |||
virtual void GetDisplay(char *text){(*fZone>0.5f)? std::strcpy(text,"ON"): std::strcpy(text,"OFF");} | |||
}; | |||
#define MAX_PARAM_DISPLAY_PRECISION 6 | |||
/*--------------------------------------------------------------------------*/ | |||
class vstSlider : public vstUIObject{ | |||
private: | |||
float fInit; | |||
float fMin; | |||
float fMax; | |||
float fStep; | |||
int fPrecision; | |||
public: | |||
vstSlider(const char* label, float* zone, float init, float min, float max, float step) | |||
:vstUIObject(label,zone), fInit(init), fMin(min), fMax(max), fStep(step), fPrecision(0) { | |||
for (fPrecision = 0; fPrecision < MAX_PARAM_DISPLAY_PRECISION && step != floor(step); ++fPrecision, step *= 10.0) {;} | |||
} | |||
virtual ~vstSlider() {} | |||
// The VST host calls GetValue() and expects a result in [0,1]. | |||
// The VST host calls SetValue(f) with f in [0,1]. We convert to real units. | |||
// When we process MIDI controls, we call SetValueNoNormalization(f) with f in real units. | |||
virtual float GetValue() { | |||
return (*fZone-fMin)/(fMax-fMin); // normalize | |||
} | |||
virtual void SetValue(double f) { | |||
*fZone = normalize(fMin,fMax,(float)f); // denormalize | |||
} | |||
virtual void SetValueNoNormalization(double f) { | |||
*fZone = clip(fMin,fMax,(float)f); // raw | |||
} | |||
int getMinValue() const { | |||
return fMin; | |||
} | |||
int getMaxValue() const { | |||
return fMax; | |||
} | |||
int getStep() const { | |||
return fStep; | |||
} | |||
void GetDisplay(char *text) { | |||
if(fPrecision == 0) { | |||
std::sprintf(text, "%d", int(*fZone)); | |||
} else { | |||
std::sprintf(text,"%.*f", fPrecision, *fZone); | |||
} | |||
} | |||
}; | |||
/*--------------------------------------------------------------------------*/ | |||
class vstUI : public UI | |||
{ | |||
private: | |||
bool fStopped; | |||
vector<vstUIObject*> fUITable; | |||
map<const float*, Meta*> m_controlMetadataMap; | |||
public: | |||
int freqIndex; // note frequency | |||
int gainIndex; // note velocity | |||
int gateIndex; // note on/off | |||
// can be used for effects such as portamento where | |||
// we have to know the previous note | |||
int prevFreqIndex; | |||
// pitchbend wheel | |||
int pitchbendIndex; | |||
// Constructor | |||
vstUI() | |||
: fStopped(false), freqIndex(-1), gainIndex(-1), gateIndex(-1) | |||
{ } | |||
// Destructor | |||
virtual ~vstUI() | |||
{ | |||
for (vector<vstUIObject*>::iterator iter = fUITable.begin(); | |||
iter != fUITable.end(); iter++) { | |||
delete *iter; | |||
} | |||
for (map<const float*, Meta*>::iterator iter = m_controlMetadataMap.begin(); | |||
iter != m_controlMetadataMap.end(); ++iter) | |||
{ | |||
delete iter->second; | |||
} | |||
} // end of destructor | |||
void stop() { fStopped = true; } | |||
bool stopped() { return fStopped; } | |||
virtual void declare(FAUSTFLOAT* zone, const char* key, const char* value) { | |||
map<const float*, Meta*>::iterator iter = m_controlMetadataMap.find(zone); | |||
if (iter == m_controlMetadataMap.end()) { | |||
// if Meta for zone doesn't exist, create it now | |||
pair<const float*, Meta*> entry(zone, new Meta()); | |||
pair< map<const float*, Meta*>::iterator, bool> result = m_controlMetadataMap.insert(entry); | |||
if (!result.second) { | |||
TRACE(fprintf(stderr, "Failed adding metadata %s:%s\n", key, value)); | |||
return; | |||
} | |||
iter = result.first; | |||
} | |||
iter->second->declare(key, value); | |||
} // end of declare | |||
/* | |||
Return metadata for control (such as style, unit, etc...). | |||
*/ | |||
const char* getControlMetadata(int index, const char* key, const char* defaultString) | |||
{ | |||
if (index < 0 || index > (int)fUITable.size()) { | |||
TRACE(fprintf(stderr, "Illegal index (%d) accessed by getControlMetadata\n", | |||
index)); | |||
return defaultString; | |||
} | |||
const float* zone = fUITable[index]->getZone(); | |||
map<const float*, Meta*>::iterator iter = m_controlMetadataMap.find(zone); | |||
if (iter == m_controlMetadataMap.end()) { | |||
TRACE(fprintf(stderr, "Metadata for control %d not found\n", index)); | |||
return defaultString; | |||
} | |||
return iter->second->get(key, defaultString); | |||
} // end of getControlMetadata | |||
void setAny(int anyIndex, float val, const char *str) { | |||
if (anyIndex < 0) { | |||
// On the Receptor, and perhaps other hosts, output to stderr is | |||
// logged in a file. | |||
TRACE(fprintf(stderr,"*** Faust vsti: %sIndex = %d never set!\n", | |||
str,anyIndex) ); | |||
return; | |||
} | |||
if (anyIndex >= (int)fUITable.size()) { | |||
TRACE(fprintf(stderr,"*** Faust vsti: %sIndex = %d too large!\n", | |||
str,anyIndex)); | |||
return; | |||
} | |||
TRACE(fprintf(stderr,"*** Faust vsti: Setting %sIndex = %d to %f\n", | |||
str,anyIndex,val)); | |||
fUITable[anyIndex]->SetValueNoNormalization(val); | |||
} // end of setAny | |||
float getAny(int anyIndex, const char* str) { | |||
if (anyIndex < 0) { | |||
TRACE(fprintf(stderr, "=== Faust VSTi: %sIndex = %d never set!\n", | |||
str, anyIndex)); | |||
return -1; | |||
} | |||
return fUITable[anyIndex]->GetValueNoNormalization(); | |||
} // end of getAny | |||
void setFreq(float val) { | |||
setAny(freqIndex, val, "freq"); | |||
} | |||
float getFreq( void ) { | |||
return getAny(freqIndex, "freq"); | |||
} | |||
void setGate(float val) { | |||
setAny(gateIndex, val, "gate"); | |||
} | |||
void setGain(float val) { | |||
setAny(gainIndex, val, "gain"); | |||
} | |||
void setPrevFreq(float val) { | |||
setAny(prevFreqIndex, val, "prevfreq"); | |||
} | |||
void setPitchBend(float val) { | |||
setAny(pitchbendIndex, val, "pitchbend"); | |||
} | |||
bool ckAnyMatch(const char* label, const char* indexName, int *index) { | |||
if (0 == strcmp(label,indexName)) { | |||
TRACE( fprintf(stderr,"=== Faust vsti: label '%s' matches '%s'\n",label,indexName) ); | |||
*index = fUITable.size() - 1; | |||
return true; | |||
} | |||
return false; | |||
} | |||
void ckAllMatches(const char* label) { | |||
ckAnyMatch(label,"gain",&gainIndex); | |||
ckAnyMatch(label,"gate",&gateIndex); | |||
ckAnyMatch(label,"freq",&freqIndex); | |||
ckAnyMatch(label,"prevfreq", &prevFreqIndex); | |||
ckAnyMatch(label,"pitchbend", &pitchbendIndex); | |||
} | |||
void addButton(const char* label, float* zone) { | |||
vstButton* theButton = new vstButton(label, zone); | |||
fUITable.push_back(theButton); | |||
TRACE( fprintf(stderr,"=== Faust vsti: Adding Button with label '%s'\n",label) ); | |||
ckAnyMatch(label,"gate",&gateIndex); | |||
} | |||
void addToggleButton(const char* label, float* zone) { | |||
fUITable.push_back(new vstToggleButton(label, zone)); | |||
} | |||
void addCheckButton(const char* label, float* zone) { | |||
fUITable.push_back(new vstCheckButton(label, zone)); | |||
} | |||
void addVerticalSlider(const char* label, float* zone, float init, | |||
float min, float max, float step) | |||
{ | |||
vstSlider* theSlider = new vstSlider(label, zone, init, min, max, step); | |||
fUITable.push_back(theSlider); | |||
TRACE( fprintf(stderr,"=== Faust vsti: Adding VSlider (HSlider) " | |||
"with label '%s'\n",label) ); | |||
ckAllMatches(label); | |||
} | |||
void addHorizontalSlider(const char* label, float* zone, float init, | |||
float min, float max, float step) | |||
{ | |||
vstSlider* theSlider = new vstSlider(label, zone, init, min, max, step); | |||
fUITable.push_back(theSlider); | |||
TRACE( fprintf(stderr,"=== Faust vsti: Adding HSlider with label '%s'\n",label) ); | |||
ckAllMatches(label); | |||
} | |||
void addNumEntry(const char* label, float* zone, float init, float min, | |||
float max, float step) | |||
{ | |||
/* Number entries converted to horizontal sliders */ | |||
vstSlider* theSlider = new vstSlider(label, zone, init, min, max, step); | |||
fUITable.push_back(theSlider); | |||
TRACE( fprintf(stderr,"=== Faust vsti: Adding NumEntry (HSlider) with " | |||
"label '%s'\n",label) ); | |||
ckAllMatches(label); | |||
} | |||
void openFrameBox(const char* label) {} | |||
void openTabBox(const char* label) {} | |||
void openHorizontalBox(const char* label) {} | |||
void openVerticalBox(const char* label) {} | |||
void closeBox() {} | |||
void SetValue(VstInt32 index, double f) { | |||
assert(index < (VstInt32)fUITable.size()); | |||
fUITable[index]->SetValue(f); | |||
} | |||
float GetValue(VstInt32 index) { | |||
assert(index < (VstInt32)fUITable.size()); | |||
return fUITable[index]->GetValue(); | |||
} | |||
void GetDisplay(VstInt32 index, char *text) { | |||
assert(index < (VstInt32)fUITable.size()); | |||
fUITable[index]->GetDisplay(text); | |||
} | |||
void GetName(VstInt32 index, char *text) { | |||
assert(index < (VstInt32)fUITable.size()); | |||
fUITable[index]->GetName(text); | |||
} | |||
long GetNumParams() { | |||
return fUITable.size(); | |||
} | |||
/* Creates a (unique?)id by summing all the parameter's labels, | |||
* then wrapping it in the range [0;maxNumberOfId] and adding | |||
* this number to the offset made by the Four Character ID: 'FAUS' | |||
*/ | |||
long makeID() { | |||
const long maxNumberOfId = 128; | |||
long baseid = 'FAUS'; | |||
long id=0; | |||
for(int i=0;i<(int)fUITable.size();i++) { | |||
id += fUITable[i]->GetID(); | |||
} | |||
return baseid + id % maxNumberOfId; | |||
} | |||
// To be implemented | |||
void addNumDisplay(const char* label, float* zone, int precision) {} | |||
void addTextDisplay(const char* label, float* zone, char* names[], | |||
float min, float max){} | |||
void addHorizontalBargraph(const char* label, float* zone, float min, | |||
float max){} | |||
void addVerticalBargraph(const char* label, float* zone, float min, | |||
float max){} | |||
}; // end of vstUI class | |||
#endif |