@@ -71,6 +71,7 @@ else | |||||
$(MAKE) -C tal-reverb-2/LV2 | $(MAKE) -C tal-reverb-2/LV2 | ||||
$(MAKE) -C tal-reverb-3/LV2 | $(MAKE) -C tal-reverb-3/LV2 | ||||
$(MAKE) -C tal-vocoder-2/LV2 | $(MAKE) -C tal-vocoder-2/LV2 | ||||
$(MAKE) -C temper/LV2 | |||||
$(MAKE) -C vex/LV2 | $(MAKE) -C vex/LV2 | ||||
$(MAKE) -C wolpertinger/LV2 | $(MAKE) -C wolpertinger/LV2 | ||||
endif | endif | ||||
@@ -111,6 +112,7 @@ vst: libs | |||||
$(MAKE) -C tal-reverb-2/VST | $(MAKE) -C tal-reverb-2/VST | ||||
$(MAKE) -C tal-reverb-3/VST | $(MAKE) -C tal-reverb-3/VST | ||||
$(MAKE) -C tal-vocoder-2/VST | $(MAKE) -C tal-vocoder-2/VST | ||||
$(MAKE) -C temper/VST | |||||
$(MAKE) -C vex/VST | $(MAKE) -C vex/VST | ||||
$(MAKE) -C wolpertinger/VST | $(MAKE) -C wolpertinger/VST | ||||
@@ -150,6 +152,7 @@ clean: | |||||
$(MAKE) clean -C tal-reverb-2/LV2 | $(MAKE) clean -C tal-reverb-2/LV2 | ||||
$(MAKE) clean -C tal-reverb-3/LV2 | $(MAKE) clean -C tal-reverb-3/LV2 | ||||
$(MAKE) clean -C tal-vocoder-2/LV2 | $(MAKE) clean -C tal-vocoder-2/LV2 | ||||
$(MAKE) clean -C temper/LV2 | |||||
$(MAKE) clean -C vex/LV2 | $(MAKE) clean -C vex/LV2 | ||||
$(MAKE) clean -C wolpertinger/LV2 | $(MAKE) clean -C wolpertinger/LV2 | ||||
@@ -185,6 +188,7 @@ clean: | |||||
$(MAKE) clean -C tal-reverb-2/VST | $(MAKE) clean -C tal-reverb-2/VST | ||||
$(MAKE) clean -C tal-reverb-3/VST | $(MAKE) clean -C tal-reverb-3/VST | ||||
$(MAKE) clean -C tal-vocoder-2/VST | $(MAKE) clean -C tal-vocoder-2/VST | ||||
$(MAKE) clean -C temper/VST | |||||
$(MAKE) clean -C vex/VST | $(MAKE) clean -C vex/VST | ||||
$(MAKE) clean -C wolpertinger/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 |