├── logo ├── logo.ai ├── logo.png └── logo_text.png ├── gui ├── syntacts.ico ├── syntacts.rc ├── src │ ├── Widget.cpp │ ├── Debugger.hpp │ ├── Widget.hpp │ ├── Designer.hpp │ ├── Player.hpp │ ├── Visualizer.hpp │ ├── Workspace.hpp │ ├── main.cpp │ ├── Gui.hpp │ ├── Palette.hpp │ ├── StatusBar.hpp │ ├── Theme.hpp │ ├── DeviceBar.hpp │ ├── FileWatcher.hpp │ ├── PolyBezier.hpp │ ├── Library.hpp │ ├── Sequencer.hpp │ ├── Visualizer.cpp │ ├── Workspace.cpp │ ├── Designer.cpp │ ├── DragAndDrop.hpp │ ├── Spatializer.hpp │ ├── FileWatcher.cpp │ ├── Custom.hpp │ ├── Gui.cpp │ ├── DragAndDrop.cpp │ ├── Debugger.cpp │ └── Palette.cpp └── CMakeLists.txt ├── unity └── SyntactsDemo │ ├── ProjectSettings │ ├── ProjectVersion.txt │ ├── ClusterInputManager.asset │ ├── PresetManager.asset │ ├── EditorBuildSettings.asset │ ├── XRSettings.asset │ ├── TimeManager.asset │ ├── VFXManager.asset │ ├── AudioManager.asset │ ├── TagManager.asset │ ├── UnityConnectSettings.asset │ ├── EditorSettings.asset │ ├── DynamicsManager.asset │ ├── NavMeshAreas.asset │ ├── Physics2DSettings.asset │ └── GraphicsSettings.asset │ ├── Assets │ ├── Syntacts │ │ ├── Editor │ │ │ ├── logo.png │ │ │ ├── SyntactsHubEditor.cs.meta │ │ │ └── logo.png.meta │ │ ├── Editor.meta │ │ ├── Plugins.meta │ │ ├── Syntacts.cs.meta │ │ ├── SyntactsHub.cs.meta │ │ ├── Plugins │ │ │ └── syntacts_c.dll.meta │ │ └── SyntactsHub.cs │ ├── Demo │ │ ├── Demo.unity.meta │ │ ├── BouncyBall.mat.meta │ │ ├── BouncyBall.physicMaterial.meta │ │ ├── BouncyBall.cs.meta │ │ ├── LibraryButton.cs.meta │ │ ├── BouncyBall.physicMaterial │ │ ├── BouncyBall.cs │ │ ├── LibraryButton.cs │ │ └── BouncyBall.mat │ ├── Demo.meta │ └── Syntacts.meta │ ├── .gitignore │ └── Packages │ └── manifest.json ├── template ├── .gitignore ├── main.cpp └── CMakeLists.txt ├── csharp ├── Syntacts │ └── Syntacts.csproj ├── README.md ├── examples │ ├── example_basic │ │ ├── example_basic.csproj │ │ └── example_basic.cs │ ├── example_music │ │ ├── example_music.csproj │ │ └── example_music.cs │ ├── example_devices │ │ ├── example_devices.csproj │ │ └── example_devices.cs │ ├── example_library │ │ ├── example_library.csproj │ │ └── example_library.cs │ ├── example_signals │ │ ├── example_signals.csproj │ │ └── example_signals.cs │ ├── example_sequences │ │ ├── example_sequences.csproj │ │ └── example_sequences.cs │ └── example_spatializer │ │ ├── example_spatializer.csproj │ │ └── example_spatializer.cs └── .gitignore ├── tests ├── CMakeLists.txt ├── dll.cpp └── benchmark.cpp ├── .gitmodules ├── .gitignore ├── python ├── example_devices.py ├── example_spatializer.py ├── example.py ├── example_music.py ├── example_sequences.py ├── example_toh.py ├── example_library.py ├── .gitignore └── example_basic.py ├── src ├── Tact │ ├── Operator.cpp │ ├── Signal.cpp │ ├── Oscillator.cpp │ ├── Sequence.cpp │ ├── MemoryPool.cpp │ ├── Process.cpp │ ├── Envelope.cpp │ └── Util.cpp └── Filesystem.hpp ├── .vscode └── cmake-variants.yaml ├── examples ├── CMakeLists.txt ├── example_rtti.cpp ├── example_spatializer.cpp ├── example_devices.cpp ├── example_sequences.cpp ├── example_signals.cpp ├── example_music.cpp ├── example_library.cpp └── example_basic.cpp ├── include ├── Tact │ ├── Detail │ │ ├── Sequence.inl │ │ ├── Oscillator.inl │ │ ├── Operator.inl │ │ └── Signal.inl │ ├── Error.hpp │ ├── Serialization.hpp │ ├── Library.hpp │ ├── Process.hpp │ ├── Config.hpp │ ├── Util.hpp │ ├── Operator.hpp │ ├── Sequence.hpp │ ├── Oscillator.hpp │ └── Spatializer.hpp └── syntacts ├── cmake └── SyntactsConfig.cmake.in ├── LICENSE ├── TODO.md ├── c └── CMakeLists.txt └── README.md /logo/logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahilab/Syntacts/HEAD/logo/logo.ai -------------------------------------------------------------------------------- /logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahilab/Syntacts/HEAD/logo/logo.png -------------------------------------------------------------------------------- /gui/syntacts.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahilab/Syntacts/HEAD/gui/syntacts.ico -------------------------------------------------------------------------------- /gui/syntacts.rc: -------------------------------------------------------------------------------- 1 | 2 | GLFW_ICON ICON "syntacts.ico" 3 | 4 | -------------------------------------------------------------------------------- /logo/logo_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahilab/Syntacts/HEAD/logo/logo_text.png -------------------------------------------------------------------------------- /gui/src/Widget.cpp: -------------------------------------------------------------------------------- 1 | #include "Widget.hpp" 2 | #include "Gui.hpp" 3 | 4 | Widget::Widget(Gui& _gui) : gui(_gui) { } -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2019.4.11f1 2 | m_EditorVersionWithRevision: 2019.4.11f1 (2d9804dddde7) 3 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts/Editor/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mahilab/Syntacts/HEAD/unity/SyntactsDemo/Assets/Syntacts/Editor/logo.png -------------------------------------------------------------------------------- /template/.gitignore: -------------------------------------------------------------------------------- 1 | # C++ 2 | [Bb]uild/ 3 | [Bb]uild*/ 4 | ipch/ 5 | .vscode/ 6 | .vs/ 7 | 8 | *.dll 9 | *.lib 10 | *.exe 11 | *.dylib 12 | *.so 13 | 14 | .DS_Store -------------------------------------------------------------------------------- /csharp/Syntacts/Syntacts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!236 &1 4 | ClusterInputManager: 5 | m_ObjectHideFlags: 0 6 | m_Inputs: [] 7 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1386491679 &1 4 | PresetManager: 5 | m_ObjectHideFlags: 0 6 | m_DefaultList: [] 7 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo/Demo.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9fc0d4010bbf28b4594072e72b8655ab 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /gui/src/Debugger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.hpp" 4 | 5 | class Debugger : public Widget { 6 | public: 7 | using Widget::Widget; 8 | void update() override; 9 | public: 10 | bool show = false; 11 | }; -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(dll dll.cpp) 2 | target_link_libraries(dll syntacts_c) 3 | target_include_directories(dll PUBLIC "../c/") 4 | 5 | add_executable(benchmark benchmark.cpp) 6 | target_link_libraries(benchmark syntacts) -------------------------------------------------------------------------------- /template/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace tact; 4 | 5 | int main(int argc, char const *argv[]) 6 | { 7 | Session s; 8 | s.open(); 9 | s.playAll(Sine(440)); 10 | sleep(2); 11 | return 0; 12 | } -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 81435aa2cb7d9af4bb9afc03ded282ff 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 83938bb5fc072fa4b813fe3730dfe30e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 687b9aefb4373ad488342349228ab146 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts/Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a7499f17ea51c9848a478978e16de643 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1045 &1 4 | EditorBuildSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Scenes: [] 8 | m_configObjects: {} 9 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/XRSettings.asset: -------------------------------------------------------------------------------- 1 | { 2 | "m_SettingKeys": [ 3 | "VR Device Disabled", 4 | "VR Device User Alert" 5 | ], 6 | "m_SettingValues": [ 7 | "False", 8 | "False" 9 | ] 10 | } -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo/BouncyBall.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e6ef143084cd27945bac1e2482d4acd6 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 2100000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo/BouncyBall.physicMaterial.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c34c7a1783e9c8d439b26c5023739966 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 13400000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /gui/src/Widget.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class Gui; 6 | 7 | class Widget { 8 | public: 9 | Widget(Gui& gui); 10 | virtual void update() { }; 11 | public: 12 | mahi::gui::Vec2 position, size; 13 | Gui& gui; 14 | }; -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!5 &1 4 | TimeManager: 5 | m_ObjectHideFlags: 0 6 | Fixed Timestep: 0.02 7 | Maximum Allowed Timestep: 0.33333334 8 | m_TimeScale: 1 9 | Maximum Particle Timestep: 0.03 10 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo/BouncyBall.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0665b2bea6d2aee4cb5d999e8bf817d5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /csharp/README.md: -------------------------------------------------------------------------------- 1 | First, build Syntacts from the `csharp/Syntacts` directory: 2 | 3 | ```shell 4 | > dotnet build 5 | ``` 6 | 7 | Then, change directories to the desired example and run it: 8 | 9 | ```shell 10 | > cd ../example_basic 11 | > dotnet run 12 | ``` 13 | 14 | Ensure that each example folder contains the C library `syntactsc.dll`. -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo/LibraryButton.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8c84d9db8be04f84c99cd30e0e326aa1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /gui/src/Designer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.hpp" 4 | #include "Nodes.hpp" 5 | 6 | class Designer : public Widget { 7 | public: 8 | Designer(Gui& _gui); 9 | tact::Signal buildSignal(); 10 | void update() override; 11 | void edit(const tact::Signal& sig); 12 | private: 13 | std::shared_ptr m_root; 14 | }; -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/mahi-gui"] 2 | path = 3rdparty/mahi-gui 3 | url = https://github.com/mahilab/mahi-gui 4 | [submodule "3rdparty/cereal"] 5 | path = 3rdparty/cereal 6 | url = https://github.com/USCiLab/cereal 7 | [submodule "3rdparty/portaudio"] 8 | path = 3rdparty/portaudio 9 | url = https://git.assembla.com/portaudio.git 10 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts/Editor/SyntactsHubEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d138f055a43604b4386663dd65a2d282 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /csharp/examples/example_basic/example_basic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp3.1 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /csharp/examples/example_music/example_music.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp3.1 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /csharp/examples/example_devices/example_devices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp3.1 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /csharp/examples/example_library/example_library.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp3.1 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /csharp/examples/example_signals/example_signals.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp3.1 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/VFXManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!937362698 &1 4 | VFXManager: 5 | m_ObjectHideFlags: 0 6 | m_IndirectShader: {fileID: 0} 7 | m_CopyBufferShader: {fileID: 0} 8 | m_SortShader: {fileID: 0} 9 | m_RenderPipeSettingsPath: 10 | m_FixedTimeStep: 0.016666668 11 | m_MaxDeltaTime: 0.05 12 | -------------------------------------------------------------------------------- /csharp/examples/example_sequences/example_sequences.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp3.1 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /csharp/examples/example_spatializer/example_spatializer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp3.1 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ 2 | [Bb]uild/ 3 | [Bb]uild*/ 4 | ipch/ 5 | .vscode/* 6 | .vs/ 7 | !.vscode/cmake-variants.yaml 8 | 9 | *.dll 10 | *.lib 11 | *.exe 12 | *.dylib 13 | *.so 14 | 15 | asiosdk/ 16 | asiosdk*/ 17 | asiosdk.zip 18 | asiosdk*.zip 19 | asio.zip 20 | 21 | relative/ 22 | *.sig 23 | *.wav 24 | *.wave 25 | *.aiff 26 | *.aifc 27 | *.csv 28 | 29 | .DS_Store 30 | 31 | debug.log -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts/Syntacts.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e97b0ce0e31aeb042b61bf0bed3081e8 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {fileID: 2800000, guid: f37ce78e955c1eb4cb8c25996487548b, type: 3} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts/SyntactsHub.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e10de8f2496aaef49ab143f2d911b04b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {fileID: 2800000, guid: f37ce78e955c1eb4cb8c25996487548b, type: 3} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo/BouncyBall.physicMaterial: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!134 &13400000 4 | PhysicMaterial: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_Name: BouncyBall 10 | dynamicFriction: 0 11 | staticFriction: 0 12 | bounciness: 1 13 | frictionCombine: 2 14 | bounceCombine: 3 15 | -------------------------------------------------------------------------------- /gui/src/Player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.hpp" 4 | 5 | class Player : public Widget { 6 | public: 7 | 8 | Player(Gui& gui); 9 | void update() override; 10 | 11 | private: 12 | 13 | void updateChannels(); 14 | void playCh(int ch); 15 | void playSelected(); 16 | void rechannel(); 17 | 18 | private: 19 | int m_payload; 20 | float m_masterVol = 1.0f; 21 | float m_masterPitch = 0.0f; 22 | }; -------------------------------------------------------------------------------- /python/example_devices.py: -------------------------------------------------------------------------------- 1 | from syntacts import * 2 | 3 | session = Session() 4 | 5 | for dev in session.available_devices: 6 | print("") 7 | print("Index: ", dev.index) 8 | print("Name: ", dev.name) 9 | print("Default: ", dev.is_default) 10 | print("API: ", dev.api_name) 11 | print("API Default: ", dev.is_api_default) 12 | print("Max Channels:", dev.max_channels) 13 | print("Sample Rates:", dev.sample_rates) -------------------------------------------------------------------------------- /gui/src/Visualizer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.hpp" 4 | 5 | class Visualizer : public Widget { 6 | public: 7 | Visualizer(Gui& gui); 8 | void setRenderedSignal(tact::Signal sig, mahi::gui::Color color); 9 | void update() override; 10 | private: 11 | float m_tl = 0, m_tr = 1; 12 | bool m_lg = false, m_rg = false, m_cg = false; 13 | tact::Signal m_signal; 14 | mahi::gui::Color m_color; 15 | std::vector m_points; 16 | }; -------------------------------------------------------------------------------- /gui/src/Workspace.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Designer.hpp" 3 | #include "Sequencer.hpp" 4 | #include "Spatializer.hpp" 5 | 6 | class Workspace : public Widget { 7 | public: 8 | enum Tab { 9 | TabDesigner, 10 | TabSequencer, 11 | TabSpatializer 12 | }; 13 | Workspace(Gui& gui); 14 | void update() override; 15 | public: 16 | Tab activeTab; 17 | Designer designer; 18 | Sequencer sequencer; 19 | Spatializer spatializer; 20 | }; -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!11 &1 4 | AudioManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Volume: 1 8 | Rolloff Scale: 1 9 | Doppler Factor: 1 10 | Default Speaker Mode: 2 11 | m_SampleRate: 0 12 | m_DSPBufferSize: 1024 13 | m_VirtualVoiceCount: 512 14 | m_RealVoiceCount: 32 15 | m_SpatializerPlugin: 16 | m_AmbisonicDecoderPlugin: 17 | m_DisableAudio: 0 18 | m_VirtualizeEffects: 1 19 | m_RequestedDSPBufferSize: 1024 20 | -------------------------------------------------------------------------------- /gui/src/main.cpp: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG 2 | #define MAHI_GUI_NO_CONSOLE 3 | // #define MAHI_GUI_USE_DISCRETE_GPU 4 | #endif 5 | #include "Gui.hpp" 6 | 7 | int main(int argc, char const *argv[]) 8 | { 9 | mahi::gui::Application::Config config; 10 | 11 | config.title = "Syntacts"; 12 | config.width = 960; 13 | config.height = 540; 14 | config.resizable = false; 15 | config.msaa = true; 16 | config.center = true; 17 | // config.decorated = false; 18 | 19 | Gui gui(config); 20 | gui.run(); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!78 &1 4 | TagManager: 5 | serializedVersion: 2 6 | tags: [] 7 | layers: 8 | - Default 9 | - TransparentFX 10 | - Ignore Raycast 11 | - 12 | - Water 13 | - UI 14 | - 15 | - 16 | - 17 | - 18 | - 19 | - 20 | - 21 | - 22 | - 23 | - 24 | - 25 | - 26 | - 27 | - 28 | - 29 | - 30 | - 31 | - 32 | - 33 | - 34 | - 35 | - 36 | - 37 | - 38 | - 39 | - 40 | m_SortingLayers: 41 | - name: Default 42 | uniqueID: 0 43 | locked: 0 44 | -------------------------------------------------------------------------------- /src/Tact/Operator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace tact 5 | { 6 | 7 | IOperator::IOperator(Signal _lhs, Signal _rhs) : 8 | lhs(std::move(_lhs)), rhs(std::move(_rhs)) 9 | { } 10 | 11 | double Sum::sample(double t) const { 12 | return lhs.sample(t) + rhs.sample(t); 13 | } 14 | 15 | double Sum::length() const { 16 | return std::max(lhs.length(), rhs.length()); 17 | } 18 | 19 | double Product::sample(double t) const { 20 | return lhs.sample(t) * rhs.sample(t); 21 | } 22 | 23 | double Product::length() const { 24 | return std::min(lhs.length(), rhs.length()); 25 | } 26 | 27 | } // namespace tact 28 | -------------------------------------------------------------------------------- /.vscode/cmake-variants.yaml: -------------------------------------------------------------------------------- 1 | buildType: 2 | default: release 3 | description: "Build Type" 4 | choices: 5 | release: 6 | short: Release 7 | long: "Building with optimizations" 8 | buildType: Release 9 | debug: 10 | short: Debug 11 | long: "Build with debugging info" 12 | buildType: Debug 13 | 14 | static: 15 | default: off 16 | choices: 17 | on: 18 | short: Static Runtime 19 | long: "Build with static runtime linkage" 20 | settings: 21 | SYNTACTS_USE_STATIC_STD_LIBS: ON 22 | off: 23 | short: Dynamic Runtime 24 | long: "Build with dynamic runtime linkage" 25 | settings: 26 | SYNTACTS_USE_STATIC_STD_LIBS: OFF -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | macro(syntacts_example SYNTACTS_EXAMPLE_NAME) 2 | add_executable(${SYNTACTS_EXAMPLE_NAME} "example_${SYNTACTS_EXAMPLE_NAME}.cpp") 3 | target_link_libraries(${SYNTACTS_EXAMPLE_NAME} syntacts) 4 | set_target_properties(${SYNTACTS_EXAMPLE_NAME} PROPERTIES DEBUG_POSTFIX -d) 5 | install(TARGETS ${SYNTACTS_EXAMPLE_NAME} 6 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 7 | ) 8 | endmacro() 9 | 10 | syntacts_example(basic) 11 | syntacts_example(signals) 12 | syntacts_example(devices) 13 | syntacts_example(library) 14 | syntacts_example(music) 15 | syntacts_example(rtti) 16 | syntacts_example(sequences) 17 | syntacts_example(spatializer) -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts/Plugins/syntacts_c.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eb673f43654b2ce48b14c239ca99eea3 3 | PluginImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | iconMap: {} 7 | executionOrder: {} 8 | defineConstraints: [] 9 | isPreloaded: 0 10 | isOverridable: 0 11 | isExplicitlyReferenced: 0 12 | validateReferences: 1 13 | platformData: 14 | - first: 15 | Any: 16 | second: 17 | enabled: 1 18 | settings: {} 19 | - first: 20 | Editor: Editor 21 | second: 22 | enabled: 0 23 | settings: 24 | DefaultValueInitialized: true 25 | userData: 26 | assetBundleName: 27 | assetBundleVariant: 28 | -------------------------------------------------------------------------------- /src/Tact/Signal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tact 4 | { 5 | 6 | Signal::Signal() : Signal(Scalar(0)) {} 7 | 8 | #ifndef SYNTACTS_USE_SHARED_PTR 9 | Signal::Signal(const Signal& other) : 10 | gain(other.gain), 11 | bias(other.bias), 12 | m_ptr(other.m_ptr->copy()) 13 | { } 14 | Signal& Signal::operator=(const Signal& other) 15 | { 16 | return *this = Signal(other); 17 | } 18 | #endif // SYNTACTS_USE_SHARED_PTR 19 | 20 | std::type_index Signal::typeId() const 21 | { 22 | return m_ptr->typeId(); 23 | } 24 | 25 | void* Signal::get() const 26 | { 27 | return m_ptr->get(); 28 | } 29 | 30 | int Signal::Concept::s_count = 0; 31 | 32 | } // namespace tact -------------------------------------------------------------------------------- /gui/src/Gui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "DeviceBar.hpp" 5 | #include "Workspace.hpp" 6 | #include "Player.hpp" 7 | #include "Visualizer.hpp" 8 | #include "StatusBar.hpp" 9 | #include "Library.hpp" 10 | #include "Theme.hpp" 11 | #include "Debugger.hpp" 12 | 13 | class Gui : public mahi::gui::Application { 14 | public: 15 | Gui(const Application::Config config); 16 | void update() override; 17 | void positionWindows(); 18 | const std::string& saveDir(); 19 | public: 20 | ThemeManager theme; 21 | StatusBar status; 22 | DeviceBar device; 23 | Player player; 24 | Workspace workspace; 25 | Library library; 26 | Visualizer visualizer; 27 | Debugger debug; 28 | }; -------------------------------------------------------------------------------- /gui/src/Palette.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "Widget.hpp" 4 | 5 | enum class PItem { 6 | Unknown = 0, 7 | Time, 8 | Scalar, 9 | Ramp, 10 | Noise, 11 | Expression, 12 | Sum, 13 | Product, 14 | Repeater, 15 | Stretcher, 16 | Reverser, 17 | Sequencer, 18 | Sine, 19 | Square, 20 | Saw, 21 | Triangle, 22 | FM, 23 | Chirp, 24 | Pwm, 25 | Envelope, 26 | ASR, 27 | ADSR, 28 | ExponentialDecay, 29 | KeyedEnvelope, 30 | SignalEnvelope, 31 | PolyBezier 32 | }; 33 | 34 | /// Returns the name of a Syntacts signal 35 | const std::string& paletteName(PItem id); 36 | 37 | class Palette : public Widget { 38 | public: 39 | using Widget::Widget; 40 | void update() override; 41 | }; -------------------------------------------------------------------------------- /gui/src/StatusBar.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.hpp" 4 | 5 | class StatusBar : public Widget { 6 | public: 7 | 8 | enum InfoLevel { 9 | Info = 0, 10 | Warning = 1, 11 | Error = 2 12 | }; 13 | 14 | StatusBar(Gui& gui); 15 | void update() override; 16 | void pushMessage(const std::string& text, InfoLevel level = Info); 17 | void showTooltip(const std::string& tooltip); 18 | private: 19 | void renderText(); 20 | void renderButtons(); 21 | private: 22 | bool m_showThemeEdit = false; 23 | mahi::gui::Sequence m_cpuGradient; 24 | mahi::gui::Sequence m_fadeColors; 25 | std::string m_notification; 26 | std::string m_tooltip; 27 | float m_fadeTime; 28 | float m_fadeDuration = 5.0f; 29 | }; -------------------------------------------------------------------------------- /template/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13.0) 2 | project(MySyntactsProject VERSION 1.0.0) 3 | 4 | # OPTION 1: find_package - use if you built and installed Syntacts to your system using CMake 5 | 6 | find_package(syntacts REQUIRED) 7 | 8 | # OPTION 2: FetchContent - use if you did not installed Syntacts and/or want to build an inplace copy 9 | 10 | # include(FetchContent) 11 | # set(SYNTACTS_BUILD_GUI OFF CACHE BOOL "" FORCE) 12 | # set(SYNTACTS_BUILD_C_DLL OFF CACHE BOOL "" FORCE) 13 | # set(SYNTACTS_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 14 | # set(SYNTACTS_BUILD_TESTS OFF CACHE BOOL "" FORCE) 15 | # FetchContent_Declare(syntacts GIT_REPOSITORY https://github.com/mahilab/Syntacts.git) 16 | # FetchContent_MakeAvailable(syntacts) 17 | 18 | add_executable(myApp main.cpp) 19 | target_link_libraries(myApp syntacts) -------------------------------------------------------------------------------- /gui/src/Theme.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Widget.hpp" 3 | 4 | struct ThemeManager : public Widget { 5 | public: 6 | 7 | enum Theme { 8 | DarkDefault = 0, 9 | LightDefault, 10 | DarkAlt1, 11 | DarkAlt2, 12 | Valve, 13 | Theme_Count 14 | }; 15 | 16 | ThemeManager(Gui& gui); 17 | ~ThemeManager(); 18 | void cycle(); 19 | void setTheme(Theme theme); 20 | void update() override; 21 | public: 22 | mahi::gui::Color libraryColor = mahi::gui::Blues::LightSkyBlue; 23 | mahi::gui::Color designerColor = mahi::gui::Purples::Plum; 24 | mahi::gui::Color sequencerColor = mahi::gui::Oranges::DarkOrange; 25 | mahi::gui::Color spatializerColor = mahi::gui::Greens::Chartreuse; 26 | bool showEditor = false; 27 | private: 28 | Theme m_theme; 29 | }; -------------------------------------------------------------------------------- /src/Tact/Oscillator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace tact 5 | { 6 | 7 | IOscillator::IOscillator() : 8 | IOscillator(100) 9 | { } 10 | 11 | IOscillator::IOscillator(double hertz) : 12 | x(std::move(TWO_PI * hertz * Time())) 13 | { } 14 | 15 | IOscillator::IOscillator(double initial, double rate) : 16 | x(std::move( ( TWO_PI * initial + PI * rate * Time() ) * Time() )) 17 | { } 18 | 19 | IOscillator::IOscillator(Signal _x) : 20 | x(std::move(_x)) 21 | { } 22 | 23 | IOscillator::IOscillator(double hertz, Signal modulation, double index) : 24 | x(std::move(TWO_PI * hertz * Time() + index * modulation)) 25 | { } 26 | 27 | Pwm::Pwm(double _frequency, double _dutyCycle) : 28 | frequency(_frequency), 29 | dutyCycle(clamp01(_dutyCycle)) 30 | { } 31 | 32 | } // namespace tact -------------------------------------------------------------------------------- /include/Tact/Detail/Sequence.inl: -------------------------------------------------------------------------------- 1 | namespace tact 2 | { 3 | 4 | inline Sequence& Sequence::operator<<(double rhs) { 5 | return push(rhs); 6 | } 7 | 8 | inline Sequence& Sequence::operator<<(Signal rhs) { 9 | return push(rhs); 10 | } 11 | 12 | inline Sequence& Sequence::operator<<(Sequence rhs) { 13 | return push(rhs); 14 | } 15 | 16 | inline Sequence operator<<(Signal lhs, Signal rhs) { 17 | Sequence seq; 18 | seq.push(lhs); 19 | seq.push(rhs); 20 | return seq; 21 | } 22 | 23 | inline Sequence operator<<(Signal lhs, double rhs) { 24 | Sequence seq; 25 | seq.push(lhs); 26 | seq.head += rhs; 27 | return seq; 28 | } 29 | 30 | inline Sequence operator<<(double lhs, Signal rhs) { 31 | Sequence seq; 32 | seq.head += lhs; 33 | seq.push(rhs); 34 | return seq; 35 | } 36 | 37 | } // namespace tact 38 | -------------------------------------------------------------------------------- /python/example_spatializer.py: -------------------------------------------------------------------------------- 1 | from syntacts import * 2 | from time import sleep 3 | from math import sin 4 | from math import pi 5 | 6 | #---------------------------------------------------------- 7 | s = Session() 8 | s.open() 9 | 10 | sig = Noise() 11 | 12 | sp = Spatializer(s) 13 | 14 | # set up channels 15 | chs = s.channel_count # choose the number of channels you want to use here 16 | spc = 1.0 / (chs - 1) 17 | for i in range(chs): 18 | sp.set_position(i, (i * spc, 0)) 19 | 20 | # set up target 21 | sp.target = (0,0) # arbitrary position 22 | sp.radius = 0.1 # arbitrtary radius 23 | 24 | # play 25 | sp.play(sig) 26 | 27 | # do something cool 28 | t = 0 29 | while (t < 10): 30 | x = 0.5 + 0.5 * sin(2*pi*t) 31 | sp.target = (x, 0) 32 | sleep(0.01) 33 | t = t + 0.01 34 | 35 | del sp # ensure Spatializer deleted before Session 36 | 37 | print("Finished!") -------------------------------------------------------------------------------- /include/Tact/Detail/Oscillator.inl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tact { 4 | 5 | double IOscillator::length() const { 6 | return INF; 7 | } 8 | 9 | inline double Sine::sample(double t) const { 10 | return std::sin(x.sample(t)); 11 | } 12 | 13 | inline double Square::sample(double t) const { 14 | return std::sin(x.sample(t)) > 0 ? 1.0 : -1.0; 15 | } 16 | 17 | inline double Saw::sample(double t) const { 18 | return -2 * INV_PI * std::atan(std::cos(0.5 * x.sample(t)) / std::sin(0.5 * x.sample(t))); 19 | } 20 | 21 | inline double Triangle::sample(double t) const { 22 | return 2 * INV_PI * std::asin(std::sin(x.sample(t))); 23 | } 24 | 25 | 26 | inline double Pwm::sample(double t) const { 27 | return std::fmod(t, 1.0 / frequency) * frequency < dutyCycle ? 1.0 : -1.0; 28 | } 29 | 30 | inline double Pwm::length() const { 31 | return INF; 32 | } 33 | 34 | } // namespace tact -------------------------------------------------------------------------------- /python/example.py: -------------------------------------------------------------------------------- 1 | from syntacts import * 2 | from time import sleep 3 | from math import sin 4 | from math import pi 5 | 6 | s = Session() 7 | s.open() 8 | 9 | x = Sine(440) * Triangle(20) * ASR(1,2,3) 10 | y = Square(440, 1000) * ADSR(1,1,1,1) 11 | z = Pwm(500,0.5) * Envelope(1) 12 | 13 | s.play(0, x) 14 | sleep(x.length) 15 | s.play(1, y) 16 | sleep(y.length) 17 | 18 | seq = Sequence() 19 | 20 | seq << 1 << x << -2 << y 21 | seq.insert(z, 4) 22 | 23 | Library.save_signal(seq,'python') 24 | loaded = Library.load_signal('python') 25 | 26 | s.play_all(loaded) 27 | sleep(loaded.length) 28 | 29 | noise = Noise() 30 | sp = Spatializer(s) 31 | sp.set_position(0, (0,0)) 32 | sp.set_position(1, (1,0)) 33 | sp.target = (0,0) 34 | sp.radius = 0.5 35 | sp.play(noise) 36 | 37 | t = 0 38 | while t < 10: 39 | xPos = 0.5 + 0.5 * sin(2*pi*t) 40 | sp.target = (xPos, 0) 41 | sleep(0.01) 42 | t += 0.01 43 | 44 | del sp -------------------------------------------------------------------------------- /gui/src/DeviceBar.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.hpp" 4 | #include 5 | 6 | class DeviceBar : public Widget { 7 | public: 8 | DeviceBar(Gui& gui); 9 | ~DeviceBar(); 10 | void initialize(); 11 | void switchDevice(const tact::Device& dev, double sampleRate = 0); 12 | void switchApi(const std::string& api); 13 | void switchSampleRate(double sampleRate); 14 | void update() override; 15 | private: 16 | void renderApiSelection(); 17 | void renderDeviceSelection(); 18 | void renderDeviceSampleRates(); 19 | void renderDeviceDetails(); 20 | void getCurrent(); 21 | void getAvailable(); 22 | public: 23 | mahi::util::Event onSessionOpen; 24 | mahi::util::Event onSessionDestroy; 25 | std::shared_ptr session; 26 | private: 27 | tact::Device m_currentDev; 28 | std::string m_currentApi; 29 | std::map> m_available; 30 | }; -------------------------------------------------------------------------------- /csharp/examples/example_devices/example_devices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Syntacts; 4 | 5 | // run with the following console command: 6 | // dotnet run 7 | 8 | class Example 9 | { 10 | static void Main(string[] args) 11 | { 12 | Session session = new Session(); 13 | 14 | foreach (Device dev in session.availableDevices) { 15 | Console.WriteLine(""); 16 | Console.WriteLine("Index: {0}", dev.index); 17 | Console.WriteLine("Name: {0}", dev.name); 18 | Console.WriteLine("Default: {0}", dev.isDefault); 19 | Console.WriteLine("API: {0}", dev.apiName); 20 | Console.WriteLine("API Default: {0}", dev.isApiDefault); 21 | Console.WriteLine("Max Channels: {0}", dev.maxChannels); 22 | Console.WriteLine("Sample Rates: [{0}]", string.Join(", ", dev.sampleRates)); 23 | } 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /examples/example_rtti.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace tact; 6 | using std::string; 7 | 8 | // This example demonstrates how a Signal can be deconstructed with RTTI once 9 | // it has been type erased. This isn't normally recommended, but under certain 10 | // circumstances it may be necessary to know what the underlying type(s) 11 | // of a Signal are (e.g. see Nodes in the GUI source code) 12 | 13 | // This example is quite advanced and only applies if you hope to load, edit, 14 | // and resave cues. 15 | 16 | void signalPrinter(const Signal& sig, int depth) { 17 | std::cout << std::string(depth*2,' ') << signalName(sig) << std::endl; 18 | } 19 | 20 | int main(int argc, char const *argv[]) { 21 | Signal x; 22 | if (!Library::loadSignal(x,"funky")) { 23 | std::cout << "Run example_music first!" << std::endl; 24 | return -1; 25 | } 26 | recurseSignal(x, signalPrinter); 27 | return 0; 28 | } -------------------------------------------------------------------------------- /examples/example_spatializer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace tact; 4 | 5 | int main(int argc, char const *argv[]) 6 | { 7 | Session ss; 8 | ss.open(); 9 | 10 | Signal sig = Noise(); 11 | 12 | // Create spatializer 13 | Spatializer sp(&ss); 14 | 15 | // set up position of channels, evenly distributed 16 | int chs = ss.getChannelCount(); 17 | double spc = 1.0 / (chs - 1); 18 | for (int i = 0; i < ss.getChannelCount(); ++i) 19 | sp.setPosition(i, i * spc); 20 | 21 | // set up target where vibration will be played 22 | sp.setTarget(0.0); 23 | sp.setRadius(0.5); 24 | 25 | // play 26 | sp.play(sig); 27 | 28 | // make moving target on pre-described path 29 | double t = 0; 30 | while (t < 10) { 31 | double x = 0.5 + 0.5 * std::sin(2*PI*t); 32 | sp.setTarget(x); 33 | sleep(0.01); 34 | t += 0.01; 35 | } 36 | 37 | std::cout << "Finished!" << std::endl; 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /gui/src/FileWatcher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | enum class FileStatus 12 | { 13 | Created, 14 | Modified, 15 | Erased 16 | }; 17 | 18 | class FileWatcher 19 | { 20 | public: 21 | // Keep a record of files from the base directory and their last modification time 22 | FileWatcher(std::string watchPath, int delayMilliseconds); 23 | // Monitor "watchPath" for changes and in case of a change execute the user supplied "action" function 24 | void start(const std::function &action); 25 | void stop(); 26 | private: 27 | std::string m_watchPath; 28 | std::chrono::duration m_delay; 29 | std::unordered_map m_paths; 30 | std::atomic_bool m_running; 31 | private: 32 | bool contains(const std::string &key); 33 | }; 34 | -------------------------------------------------------------------------------- /cmake/SyntactsConfig.cmake.in: -------------------------------------------------------------------------------- 1 | get_filename_component(SYNTACTS_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 2 | 3 | include(CMakeFindDependencyMacro) 4 | 5 | list(APPEND CMAKE_MODULE_PATH ${SYNTACTS_CMAKE_DIR}) 6 | 7 | set(SYNTACTS_CMAKE_DIR_ABOVE ${SYNTACTS_CMAKE_DIR}) 8 | get_filename_component(SYNTACTS_CMAKE_DIR_ABOVE ${SYNTACTS_CMAKE_DIR_ABOVE} DIRECTORY) 9 | set(portaudio_DIR ${SYNTACTS_CMAKE_DIR_ABOVE}/portaudio) 10 | find_package(portaudio REQUIRED) 11 | set(portaudio_INCLUDE_DIR ${portaudio_DIR}) 12 | get_filename_component(portaudio_INCLUDE_DIR ${portaudio_INCLUDE_DIR} DIRECTORY) 13 | get_filename_component(portaudio_INCLUDE_DIR ${portaudio_INCLUDE_DIR} DIRECTORY) 14 | get_filename_component(portaudio_INCLUDE_DIR ${portaudio_INCLUDE_DIR} DIRECTORY) 15 | set(portaudio_INCLUDE_DIR "${portaudio_INCLUDE_DIR}/include") 16 | 17 | list(REMOVE_AT CMAKE_MODULE_PATH -1) 18 | 19 | if(NOT TARGET syntacts::syntacts) 20 | include("${SYNTACTS_CMAKE_DIR}/SyntactsTargets.cmake") 21 | endif() 22 | 23 | set(SYNTACTS_LIBRARIES syntacts::syntacts) -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!310 &1 4 | UnityConnectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 1 7 | m_Enabled: 0 8 | m_TestMode: 0 9 | m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events 10 | m_EventUrl: https://cdp.cloud.unity3d.com/v1/events 11 | m_ConfigUrl: https://config.uca.cloud.unity3d.com 12 | m_TestInitMode: 0 13 | CrashReportingSettings: 14 | m_EventUrl: https://perf-events.cloud.unity3d.com 15 | m_Enabled: 0 16 | m_LogBufferSize: 10 17 | m_CaptureEditorExceptions: 1 18 | UnityPurchasingSettings: 19 | m_Enabled: 0 20 | m_TestMode: 0 21 | UnityAnalyticsSettings: 22 | m_Enabled: 0 23 | m_TestMode: 0 24 | m_InitializeOnStartup: 1 25 | UnityAdsSettings: 26 | m_Enabled: 0 27 | m_InitializeOnStartup: 1 28 | m_TestMode: 0 29 | m_IosGameId: 30 | m_AndroidGameId: 31 | m_GameIds: {} 32 | m_GameId: 33 | PerformanceReportingSettings: 34 | m_Enabled: 0 35 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo/BouncyBall.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Syntacts; 5 | 6 | public class BouncyBall : MonoBehaviour 7 | { 8 | public SyntactsHub syntacts; 9 | private Rigidbody rb; 10 | 11 | public int collisionChannel = 0; 12 | public int velocityChannel = 1; 13 | 14 | public float collisionFreq = 500; 15 | public float velocityFreq = 200; 16 | 17 | 18 | // Start is called before the first frame update 19 | void Start() 20 | { 21 | syntacts.session.Play(velocityChannel, new Sine(velocityFreq) * new Sine(5)); 22 | rb = GetComponent(); 23 | } 24 | 25 | // Update is called once per frame 26 | void Update() 27 | { 28 | syntacts.session.SetPitch(velocityChannel, 1 + rb.velocity.magnitude* 0.1); 29 | } 30 | 31 | void OnCollisionEnter(Collision col) { 32 | Signal collision = new Square(collisionFreq) * new ASR(0.05, 0.05, 0.05); 33 | syntacts.session.Play(collisionChannel, collision); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /gui/src/PolyBezier.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace ImGui { 6 | 7 | struct PolyBezier { 8 | PolyBezier(ImVec4 color = ImVec4(0,1,1,1), ImVec2 min = ImVec2(0,0), ImVec2 max = ImVec2(1,1)); 9 | int pointCount(); 10 | int bezierCount(); 11 | void clearPoints(); 12 | void addPoint(ImVec2 cpL, ImVec2 pos, ImVec2 cpR); 13 | void getPoint(int index, ImVec2* cpL, ImVec2* pos, ImVec2* cpR); 14 | void getBezier(int index, ImVec2* pos0, ImVec2* cp0, ImVec2* cp1, ImVec2* pos1); 15 | 16 | ImVec2 min; 17 | ImVec2 max; 18 | ImVec4 color; 19 | float thickness; 20 | int segments; 21 | bool constrainEndpoints; 22 | float grabRadius; 23 | 24 | private: 25 | friend void PolyBezierEdit(const char*,PolyBezier*,int,int,ImVec2); 26 | struct Point; 27 | struct Group; 28 | std::shared_ptr root; 29 | std::shared_ptr head; 30 | }; 31 | 32 | void PolyBezierEdit(const char* label, PolyBezier* polyBezier, int gridX = 10, int gridY = 10, ImVec2 size = ImVec2(-1,0)); 33 | 34 | } // namespace ImGui -------------------------------------------------------------------------------- /gui/src/Library.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Widget.hpp" 4 | #include "StatusBar.hpp" 5 | #include "Designer.hpp" 6 | #include 7 | #include "Palette.hpp" 8 | #include "Visualizer.hpp" 9 | #include "FileWatcher.hpp" 10 | 11 | 12 | class Library : public Widget { 13 | public: 14 | 15 | Library(Gui& gui); 16 | 17 | struct Entry { 18 | std::string name; 19 | tact::Signal disk; 20 | bool loaded = false; 21 | }; 22 | 23 | void update() override; 24 | private: 25 | void init(); 26 | void renderCreateDialog(); 27 | void renderLibraryList(); 28 | void renderLibraryControls(); 29 | tact::Signal getSelectedSignal(); 30 | private: 31 | 32 | void onFileChange(std::string path, FileStatus status); 33 | void onFileDrop(const std::vector& paths); 34 | 35 | public: 36 | Palette palette; 37 | private: 38 | std::mutex m_mtx; 39 | FileWatcher m_watcher; 40 | std::string m_selected; 41 | int m_selection = -1; 42 | char m_inputBuffer[64] = ""; 43 | std::map m_lib; 44 | bool m_ignoreFilChange = false; 45 | bool m_focused; 46 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /python/example_music.py: -------------------------------------------------------------------------------- 1 | from syntacts import * 2 | from time import sleep 3 | from math import sin 4 | from math import pi 5 | 6 | # Returns musical note corresponding to name and octave 7 | def key(name, octave): 8 | names = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'] 9 | idx = names.index(name) 10 | i = idx - 9 + (octave - 4) * 12 11 | a = 2**(1/12) 12 | freq = 440 * a**i 13 | return Square(freq) * ASR(0.05, 0.10, 0.1) 14 | 15 | 16 | funkytown = Sequence() 17 | Dsharp = key("D#",6) 18 | Csharp = key("C#",6) 19 | Asharp = key("A#",5) 20 | Gsharp = key("G#",6) 21 | G = key("G", 6) 22 | funkytown << Dsharp << Dsharp << Csharp << Dsharp << 0.2 << Asharp << 0.2 << Asharp << Dsharp << Gsharp << G << Dsharp 23 | 24 | dixie = Sequence() 25 | dixie << key("B", 5) << key("G#",5) << key("E", 5) << key("E", 5) << key("E", 5) << key("F#",5) << key("G#",5) << key("A", 5) << key("B", 5) << key("B", 5) << key("B", 5) << key("G#",5) 26 | 27 | session = Session() 28 | session.open() 29 | Library.save_signal(funkytown, "funky") 30 | session.play_all(funkytown) 31 | sleep(funkytown.length + 0.25) 32 | session.play_all(dixie) 33 | sleep(dixie.length + 0.25) 34 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo/LibraryButton.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Syntacts; 5 | 6 | public class LibraryButton : MonoBehaviour 7 | { 8 | [Tooltip("Reference to a SyntactsHub in the scene.")] 9 | public SyntactsHub syntacts; 10 | [Tooltip("Name of Library signal to load and play.")] 11 | public string signalName = "mySignal"; 12 | [Tooltip("The channel the signal will be played on.")] 13 | public int channel = 0; 14 | 15 | // Start is called before the first frame update 16 | void Start() 17 | { 18 | // make "mySignal" so demo works initially 19 | Signal mySignal = new Sine(440, 500) * new ASR(0.1,0.1,0.1); 20 | Library.SaveSignal(mySignal, "mySignal"); 21 | } 22 | 23 | void OnGUI() { 24 | if (GUI.Button(new Rect(10, 10, 125, 25), "Play Library Signal")) { 25 | Signal signal; 26 | if (Library.LoadSignal(out signal, signalName)) { 27 | syntacts.session.Play(channel, signal); 28 | } 29 | else { 30 | Debug.LogError("Failed to load signal: " + signalName); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(syntacts_gui 2 | src/main.cpp 3 | src/Gui.hpp 4 | src/Gui.cpp 5 | src/Widget.hpp 6 | src/Widget.cpp 7 | src/DeviceBar.hpp 8 | src/DeviceBar.cpp 9 | src/StatusBar.hpp 10 | src/Palette.hpp 11 | src/Palette.cpp 12 | src/StatusBar.cpp 13 | src/Visualizer.hpp 14 | src/Visualizer.cpp 15 | src/Library.hpp 16 | src/Library.cpp 17 | src/FileWatcher.hpp 18 | src/FileWatcher.cpp 19 | src/Nodes.hpp 20 | src/Nodes.cpp 21 | src/DragAndDrop.hpp 22 | src/DragAndDrop.cpp 23 | src/Workspace.hpp 24 | src/Workspace.cpp 25 | src/Designer.hpp 26 | src/Designer.cpp 27 | src/PolyBezier.hpp 28 | src/PolyBezier.cpp 29 | src/Sequencer.hpp 30 | src/Sequencer.cpp 31 | src/Spatializer.hpp 32 | src/Spatializer.cpp 33 | src/Player.hpp 34 | src/Player.cpp 35 | src/Debugger.hpp 36 | src/Debugger.cpp 37 | src/Theme.hpp 38 | src/Theme.cpp 39 | src/Custom.hpp 40 | src/Custom.cpp 41 | syntacts.rc) 42 | 43 | target_link_libraries(syntacts_gui syntacts mahi::gui) 44 | set_target_properties(syntacts_gui PROPERTIES DEBUG_POSTFIX -d) 45 | install(TARGETS syntacts_gui RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!159 &1 4 | EditorSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 9 7 | m_ExternalVersionControlSupport: Visible Meta Files 8 | m_SerializationMode: 2 9 | m_LineEndingsForNewScripts: 2 10 | m_DefaultBehaviorMode: 0 11 | m_PrefabRegularEnvironment: {fileID: 0} 12 | m_PrefabUIEnvironment: {fileID: 0} 13 | m_SpritePackerMode: 0 14 | m_SpritePackerPaddingPower: 1 15 | m_EtcTextureCompressorBehavior: 1 16 | m_EtcTextureFastCompressor: 1 17 | m_EtcTextureNormalCompressor: 2 18 | m_EtcTextureBestCompressor: 4 19 | m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref 20 | m_ProjectGenerationRootNamespace: 21 | m_CollabEditorSettings: 22 | inProgressEnabled: 1 23 | m_EnableTextureStreamingInEditMode: 1 24 | m_EnableTextureStreamingInPlayMode: 1 25 | m_AsyncShaderCompilation: 1 26 | m_EnterPlayModeOptionsEnabled: 0 27 | m_EnterPlayModeOptions: 3 28 | m_ShowLightmapResolutionOverlay: 1 29 | m_UseLegacyProbeSampleCount: 1 30 | m_AssetPipelineMode: 1 31 | m_CacheServerMode: 0 32 | m_CacheServerEndpoint: 33 | m_CacheServerNamespacePrefix: default 34 | m_CacheServerEnableDownload: 1 35 | m_CacheServerEnableUpload: 1 36 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/.gitignore: -------------------------------------------------------------------------------- 1 | # Unity 2 | 3 | # This .gitignore file should be placed at the root of your Unity project directory 4 | # 5 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 6 | # 7 | 8 | syntactsc.dll.meta 9 | libsyntactsc.dylib.meta 10 | 11 | /[Ll]ibrary/ 12 | /[Tt]emp/ 13 | /[Oo]bj/ 14 | /[Bb]uild/ 15 | /[Bb]uilds/ 16 | /[Ll]ogs/ 17 | /[Mm]emoryCaptures/ 18 | 19 | # Never ignore Asset meta data 20 | !/[Aa]ssets/**/*.meta 21 | 22 | # Uncomment this line if you wish to ignore the asset store tools plugin 23 | # /[Aa]ssets/AssetStoreTools* 24 | 25 | # Autogenerated Jetbrains Rider plugin 26 | [Aa]ssets/Plugins/Editor/JetBrains* 27 | 28 | # Visual Studio cache directory 29 | .vs/ 30 | .vscode/ 31 | 32 | # Gradle cache directory 33 | .gradle/ 34 | 35 | # Autogenerated VS/MD/Consulo solution and project files 36 | ExportedObj/ 37 | .consulo/ 38 | *.csproj 39 | *.unityproj 40 | *.sln 41 | *.suo 42 | *.tmp 43 | *.user 44 | *.userprefs 45 | *.pidb 46 | *.booproj 47 | *.svd 48 | *.pdb 49 | *.mdb 50 | *.opendb 51 | *.VC.db 52 | 53 | # Unity3D generated meta files 54 | *.pidb.meta 55 | *.pdb.meta 56 | *.mdb.meta 57 | 58 | # Unity3D generated file on crash reports 59 | sysinfo.txt 60 | 61 | # Builds 62 | *.apk 63 | *.unitypackage 64 | 65 | # Crashlytics generated file 66 | crashlytics-build.properties 67 | -------------------------------------------------------------------------------- /examples/example_devices.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace tact; 5 | 6 | // This example will load all devices and display their information for your computer. 7 | // Run the exe from a window that will stay open once it runs so you can see the results 8 | 9 | int main(int argc, char const *argv[]) 10 | { 11 | Session session; 12 | 13 | for (auto& d : session.getAvailableDevices()) 14 | { 15 | auto& dev = d.second; 16 | std::cout << std::endl; 17 | std::cout << "Index: " << dev.index << std::endl; 18 | std::cout << "Name: " << dev.name << std::endl; 19 | std::cout << "Default: " << (dev.isDefault ? "True" : "False") << std::endl; 20 | std::cout << "API: " << dev.apiName << std::endl; 21 | std::cout << "API Default: " << (dev.isApiDefault ? "True" : "False") << std::endl; 22 | std::cout << "Max Channels: " << dev.maxChannels << std::endl; 23 | std::cout << "Sample Rates: ["; 24 | for (auto& s : dev.sampleRates) 25 | std::cout << s << (s != dev.sampleRates.back() ? ", " : ""); 26 | std::cout << "]" << std::endl; 27 | } 28 | std::cout << std::endl; 29 | 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /include/syntacts: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Syntacts 4 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab - Rice University 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // Author(s): Evan Pezent, Brandon Cambio 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!55 &1 4 | PhysicsManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 11 7 | m_Gravity: {x: 0, y: -9.81, z: 0} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_BounceThreshold: 2 10 | m_SleepThreshold: 0.005 11 | m_DefaultContactOffset: 0.01 12 | m_DefaultSolverIterations: 6 13 | m_DefaultSolverVelocityIterations: 1 14 | m_QueriesHitBackfaces: 0 15 | m_QueriesHitTriggers: 1 16 | m_EnableAdaptiveForce: 0 17 | m_ClothInterCollisionDistance: 0 18 | m_ClothInterCollisionStiffness: 0 19 | m_ContactsGeneration: 1 20 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21 | m_AutoSimulation: 1 22 | m_AutoSyncTransforms: 0 23 | m_ReuseCollisionCallbacks: 1 24 | m_ClothInterCollisionSettingsToggle: 0 25 | m_ContactPairsMode: 0 26 | m_BroadphaseType: 0 27 | m_WorldBounds: 28 | m_Center: {x: 0, y: 0, z: 0} 29 | m_Extent: {x: 250, y: 250, z: 250} 30 | m_WorldSubdivisions: 8 31 | m_FrictionType: 0 32 | m_EnableEnhancedDeterminism: 0 33 | m_EnableUnifiedHeightmaps: 1 34 | m_DefaultMaxAngluarSpeed: 7 35 | -------------------------------------------------------------------------------- /csharp/examples/example_spatializer/example_spatializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Syntacts; 4 | 5 | // run with the following console command: 6 | // dotnet run 7 | 8 | class Example 9 | { 10 | static void Main(string[] args) 11 | { 12 | Session ss = new Session(); 13 | ss.Open(); 14 | 15 | Signal sig = new Noise(); 16 | 17 | // Create spatializer 18 | Spatializer sp = new Spatializer(ss); 19 | 20 | // set up position of channels, evenly distributed 21 | int chs = ss.channelCount; 22 | double spc = 1.0 / (chs - 1); 23 | for (int i = 0; i < ss.channelCount; ++i) 24 | sp.SetPosition(i, new Point(i * spc,0)); 25 | 26 | // set up target where vibration will be played 27 | sp.target = new Point(0,0); 28 | sp.radius = 0.1; 29 | 30 | // play 31 | sp.Play(sig); 32 | 33 | // make moving target on pre-described path 34 | double t = 0; 35 | while (t < 10) { 36 | double xPos = 0.5 + 0.5 * Math.Sin(2*Math.PI*t); 37 | sp.target = new Point(xPos, 0); 38 | Sleep(0.01); 39 | t += 0.01; 40 | } 41 | 42 | Console.WriteLine("Finished"); 43 | ss.Dispose(); 44 | } 45 | static void Sleep(double seconds) { 46 | Thread.Sleep((int)(seconds*1000)); 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /gui/src/Sequencer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Widget.hpp" 3 | 4 | /////////////////////////////////////////////////////////////////////////////// 5 | // CUSTOM IMGUI 6 | 7 | namespace ImGui { 8 | 9 | struct SeqInterface { 10 | 11 | struct Track { 12 | tact::Signal signal; 13 | std::string label; 14 | double t = 0; 15 | bool visible = true; 16 | bool held = false; 17 | bool populated = false; 18 | bool expanded = false; 19 | }; 20 | 21 | std::vector tracks; 22 | int selected = 0; 23 | float tMin = 0, tMax = 5, tEnd = 10; 24 | ImVec4 activeColor = {1,1,1,1}; 25 | ImVec4 inactiveColor = {0,0,0,1}; 26 | bool milliseconds = false; 27 | bool snap = true; 28 | bool lg = false, rg = false, cg = false; 29 | bool fitThisFrame = false; 30 | 31 | std::function onTooltip = [&](const char* msg){ }; 32 | }; 33 | 34 | bool Sequencer(const char* label, SeqInterface& I, bool fullFrame = true); 35 | 36 | } 37 | 38 | /////////////////////////////////////////////////////////////////////////////// 39 | 40 | class Sequencer : public Widget { 41 | public: 42 | Sequencer(Gui& gui); 43 | void update() override; 44 | tact::Signal buildSignal(); 45 | private: 46 | tact::Sequence m_seq; 47 | ImGui::SeqInterface m_interface; 48 | }; 49 | -------------------------------------------------------------------------------- /gui/src/Visualizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Visualizer.hpp" 2 | #include "Custom.hpp" 3 | #include "DragAndDrop.hpp" 4 | 5 | using namespace mahi::gui; 6 | 7 | Visualizer::Visualizer(Gui& gui) : Widget(gui), m_points(10000) { 8 | 9 | } 10 | 11 | void Visualizer::setRenderedSignal(tact::Signal sig, Color color) 12 | { 13 | m_signal = std::move(sig); 14 | m_color = color; 15 | } 16 | 17 | 18 | void Visualizer::update() 19 | { 20 | ImGui::BeginFixed("Visualizer", position, size, ImGuiWindowFlags_NoTitleBar); 21 | ImGui::BeginGroup(); 22 | float h = ImGui::GetContentRegionAvail().y - ImGui::GetStyle().ScrollbarSize - ImGui::GetStyle().ItemSpacing.y; 23 | float len = (float)m_signal.length(); 24 | len = len == tact::INF ? 1 : len; 25 | float t1 = m_tl * len; 26 | float t2 = m_tr * len; 27 | ImGui::PlotSignal("Visualizer",m_signal,m_points,t1,t2,m_color,1.0f,ImVec2(-1,h)); 28 | ImGui::TimelineScrollbar(&m_tl, &m_tr, &m_lg, &m_rg, &m_cg); 29 | ImGui::EndGroup(); 30 | if (HelpTarget()) 31 | ImGui::OpenPopup("Visualizer Help"); 32 | if (ImGui::BeginHelpPopup("Visualizer Help")) { 33 | ImGui::BulletText("The visualizer displays the waveform of the current Signal"); 34 | ImGui::BulletText("The length of the Signal is shown in the bottom right"); 35 | ImGui::BulletText("Zoom using the left and right handles of the horizontal scroll bar below"); 36 | ImGui::EndPopup(); 37 | } 38 | ImGui::End(); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /gui/src/Workspace.cpp: -------------------------------------------------------------------------------- 1 | #include "Workspace.hpp" 2 | #include "Gui.hpp" 3 | 4 | using namespace mahi::gui; 5 | 6 | Workspace::Workspace(Gui& gui) : Widget(gui), 7 | designer(gui), 8 | sequencer(gui), 9 | spatializer(gui) 10 | { 11 | } 12 | 13 | void Workspace::update() 14 | { 15 | ImGui::BeginFixed("Workspace", position, size, ImGuiWindowFlags_NoTitleBar); 16 | if (ImGui::BeginTabBar("WorkspaceTabs")) 17 | { 18 | if (ImGui::BeginTabItem(" Designer ##Tab")) 19 | { 20 | activeTab = TabDesigner; 21 | designer.update(); 22 | ImGui::EndTabItem(); 23 | gui.visualizer.setRenderedSignal(designer.buildSignal(), gui.theme.designerColor); 24 | } 25 | if (ImGui::BeginTabItem(" Sequencer ##Tab")) 26 | { 27 | activeTab = TabSequencer; 28 | sequencer.update(); 29 | ImGui::EndTabItem(); 30 | gui.visualizer.setRenderedSignal(sequencer.buildSignal(), gui.theme.sequencerColor); 31 | } 32 | if (ImGui::BeginTabItem(" Spatializer ##Tab")) 33 | { 34 | activeTab = TabSpatializer; 35 | spatializer.update(); 36 | ImGui::EndTabItem(); 37 | gui.visualizer.setRenderedSignal(spatializer.getSignal(), gui.theme.spatializerColor); 38 | } 39 | ImGui::EndTabBar(); 40 | } 41 | ImGui::End(); 42 | } -------------------------------------------------------------------------------- /gui/src/Designer.cpp: -------------------------------------------------------------------------------- 1 | #include "Designer.hpp" 2 | #include "Custom.hpp" 3 | #include "Gui.hpp" 4 | 5 | using namespace mahi::gui; 6 | 7 | Designer::Designer(Gui& _gui) : Widget(_gui) { 8 | m_root = std::make_shared(); 9 | } 10 | 11 | 12 | tact::Signal Designer::buildSignal() 13 | { 14 | return m_root->signal(); 15 | } 16 | 17 | void Designer::update() 18 | { 19 | ImGui::PushStyleColor(ImGuiCol_ChildBg, {0,0,0,0}); 20 | ImGui::BeginChild("Designer##TabScroll"); 21 | m_root->update(); 22 | ImGui::EndChild(); 23 | if (HelpTarget()) 24 | ImGui::OpenPopup("Designer Help"); 25 | if (ImGui::BeginHelpPopup("Designer Help")) { 26 | ImGui::BulletText("Drag items from the Palette or Library tabs into empty slots of the Node stack"); 27 | ImGui::BulletText("By default, the resulting output Signal will be the product of the entire Node stack"); 28 | ImGui::BulletText("Each Node has controls to configure it"); 29 | ImGui::BulletText("You can collapse Nodes by clicking their slot header"); 30 | ImGui::BulletText("Click the close button to remove Nodes from the stack"); 31 | ImGui::EndPopup(); 32 | } 33 | if (SignalTarget()) { 34 | auto pl = SignalPayload(); 35 | edit(pl.second); 36 | } 37 | ImGui::PopStyleColor(); 38 | } 39 | 40 | void Designer::edit(const tact::Signal& sig) { 41 | auto node = makeRoot(sig); 42 | if (node) 43 | m_root = node; 44 | else 45 | gui.status.pushMessage("Failed to deconstruct Signal!", StatusBar::InfoLevel::Error); 46 | } -------------------------------------------------------------------------------- /tests/dll.cpp: -------------------------------------------------------------------------------- 1 | #include "syntacts.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void sleep(double seconds) { 7 | std::this_thread::sleep_for(std::chrono::nanoseconds((std::size_t)(seconds * 1000000000))); 8 | } 9 | 10 | 11 | int main(int argc, char const *argv[]) 12 | { 13 | auto s = Session_create(); 14 | Session_open1(s); 15 | 16 | std::cout << Debug_sigMapSize() << std::endl; 17 | std::cout << Signal_count() << std::endl; 18 | 19 | auto sine1 = Sine_create2(440); 20 | auto sine2 = Sine_create2(10); 21 | auto adsr1 = ADSR_create(1,1,1,1,1,0.5); 22 | auto prod1 = Product_create(sine1, sine2); 23 | auto prod2 = Product_create(prod1, adsr1); 24 | 25 | std::cout << Debug_sigMapSize() << std::endl; 26 | std::cout << Signal_count() << std::endl; 27 | 28 | Signal_delete(sine1); 29 | Signal_delete(sine2); 30 | Signal_delete(adsr1); 31 | Signal_delete(prod1); 32 | 33 | std::cout << Debug_sigMapSize() << std::endl; 34 | std::cout << Signal_count() << std::endl; 35 | 36 | Session_play(s, 0, prod2); 37 | sleep(6); 38 | 39 | Signal_delete(prod2); 40 | 41 | std::cout << Debug_sigMapSize() << std::endl; 42 | std::cout << Signal_count() << std::endl; 43 | 44 | auto l = Library_loadSignal("funky"); 45 | Session_play(s, 0, l); 46 | sleep(Signal_length(l)); 47 | 48 | Session_delete(s); 49 | 50 | std::cout << Debug_sigMapSize() << std::endl; 51 | std::cout << Signal_count() << std::endl; 52 | 53 | std::cout << "The End" << std::endl; 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /gui/src/DragAndDrop.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Palette.hpp" 7 | 8 | /////////////////////////////////////////////////////////////////////////////// 9 | // General DND 10 | /////////////////////////////////////////////////////////////////////////////// 11 | 12 | void UpdateDragAndDrop(); 13 | void BeginPulsable(bool acceptPalette, bool acceptSignal, ImGuiCol col = ImGuiCol_Button); 14 | void EndPulsable(); 15 | 16 | /////////////////////////////////////////////////////////////////////////////// 17 | // Pallete DND 18 | /////////////////////////////////////////////////////////////////////////////// 19 | 20 | void PaletteSource(PItem pitem); 21 | bool PaletteTarget(); 22 | PItem PalettePayload(); 23 | bool PaletteHeld(); 24 | 25 | /////////////////////////////////////////////////////////////////////////////// 26 | // Signal DND 27 | /////////////////////////////////////////////////////////////////////////////// 28 | 29 | void SignalSource(const std::string& name, tact::Signal signal); 30 | bool SignalTarget(); 31 | const std::pair& SignalPayload(); 32 | bool SignalHeld(); 33 | 34 | /////////////////////////////////////////////////////////////////////////////// 35 | // Help DND 36 | /////////////////////////////////////////////////////////////////////////////// 37 | 38 | void HelpSource(); 39 | bool HelpTarget(); 40 | 41 | /////////////////////////////////////////////////////////////////////////////// 42 | // Channel DND 43 | /////////////////////////////////////////////////////////////////////////////// 44 | 45 | -------------------------------------------------------------------------------- /src/Tact/Sequence.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace tact { 6 | 7 | Sequence::Sequence() : head(0), m_keys(0), m_length(0) 8 | { 9 | } 10 | 11 | Sequence& Sequence::push(double t) { 12 | head += t; 13 | return *this; 14 | } 15 | 16 | Sequence& Sequence::push(Signal signal) { 17 | insert(signal, head); 18 | head += signal.length(); 19 | return *this; 20 | } 21 | 22 | Sequence& Sequence::push(Sequence sequence) { 23 | insert(sequence, head); 24 | head += sequence.length(); 25 | return *this; 26 | } 27 | 28 | Sequence& Sequence::insert(Signal signal, double t) 29 | { 30 | m_length = std::max(m_length, t + signal.length()); 31 | m_keys.push_back({t, std::move(signal)}); 32 | return *this; 33 | } 34 | 35 | Sequence& Sequence::insert(Sequence sequence, double t) { 36 | m_length = std::max(m_length, t + sequence.length()); 37 | for (auto& k : sequence.m_keys) 38 | insert(k.signal, t + k.t); 39 | return *this; 40 | } 41 | 42 | double Sequence::sample(double t) const { 43 | double sample = 0; 44 | for (auto& k : m_keys) { 45 | if (t >= k.t && t <= k.t + k.signal.length()) 46 | sample += k.signal.sample(t - k.t); 47 | } 48 | return sample; 49 | } 50 | 51 | double Sequence::length() const { 52 | return m_length; 53 | } 54 | 55 | void Sequence::clear() { 56 | m_keys.clear(); 57 | head = 0; 58 | m_length = 0; 59 | } 60 | 61 | int Sequence::keyCount() const { 62 | return m_keys.size(); 63 | } 64 | 65 | const Sequence::Key& Sequence::getKey(int idx) const { 66 | return m_keys[idx]; 67 | } 68 | 69 | } // namespace tact -------------------------------------------------------------------------------- /gui/src/Spatializer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Widget.hpp" 3 | #include "Custom.hpp" 4 | 5 | /////////////////////////////////////////////////////////////////////////////// 6 | // CUSTOM IMGUI 7 | 8 | namespace ImGui { 9 | 10 | struct SpatializerNode { 11 | int index; 12 | ImVec2 pos; 13 | bool held = false; 14 | float level = 0; 15 | }; 16 | 17 | struct SpatializerTarget { 18 | ImVec2 pos; 19 | float radius; 20 | }; 21 | 22 | bool Spatializer(const char* label, SpatializerTarget& target, tact::Curve rolloff, 23 | std::map& nodes, float nodeRadius, ImVec4 color, 24 | ImVec2 size, const char* dnd, int xdivs = 10, int ydivs = 10, bool snap = false, bool wrap = false); 25 | 26 | } // namespace ImGui 27 | 28 | /////////////////////////////////////////////////////////////////////////////// 29 | 30 | class Spatializer : public Widget { 31 | public: 32 | Spatializer(Gui& gui); 33 | ~Spatializer(); 34 | tact::Signal getSignal(); 35 | void update(); 36 | tact::Spatializer spatializer; 37 | private: 38 | void sync(); 39 | void fillGrid(); 40 | void onSessionChange(); 41 | void onSessionDestroy(); 42 | 43 | private: 44 | tact::Signal m_signal; 45 | int m_rollOffIndex = 0; 46 | int m_rollOffHoveredIdx = -1; 47 | std::string m_sigName = "##Empty"; 48 | int m_divs[2] = {5,5}; 49 | bool m_snap = true; 50 | bool m_2d = true; 51 | bool m_wrap = false; 52 | bool xFirst = false; 53 | char m_inputBuffer[64] = ""; 54 | std::map m_channels; 55 | ImGui::SpatializerTarget m_target; 56 | std::size_t m_openId, m_destroyId; 57 | }; -------------------------------------------------------------------------------- /python/example_sequences.py: -------------------------------------------------------------------------------- 1 | from syntacts import * 2 | from time import sleep 3 | from math import sin 4 | from math import pi 5 | 6 | #------------------------------------------------------------- 7 | 8 | s = Session() 9 | s.open() 10 | 11 | # Sequences allow to you order multiple Signals in time. 12 | 13 | # Most simply, you can concatenate two or more signals: 14 | sigA = Sine(440) * ASR(1,1,1) 15 | sigB = Square(440) * ADSR(1,1,1,1) 16 | 17 | seq1 = Sequence() 18 | seq1 << sigA << sigB 19 | 20 | print(sigA.length) # 3 s 21 | print(sigB.length) # 4 s 22 | print(seq1.length) # 7 s 23 | 24 | s.play_all(seq1) 25 | sleep(seq1.length) 26 | 27 | # You can also add delay, pause, and fade with scalar values: 28 | seq2 = Sequence() 29 | seq2 << 1 << sigA << 2 << sigB << -1 << sigA # 1 s delay, 2 s pause, 1 s fade 30 | 31 | print(seq2.length) # 12 s 32 | 33 | s.play_all(seq2) 34 | sleep(seq2.length) 35 | 36 | # Of course, Sequences of Sequences is possible: 37 | seq3 = Sequence() 38 | seq3 = seq1 << seq2 # note this will also modify seq1 39 | print(seq3.length) # 19, we won't play this one :) 40 | 41 | # The << operator inserts a Signal at the Sequence head position, 42 | # and then moves it forward or backward. You can get/set the head position 43 | # manually for the next << operation: 44 | 45 | print(seq2.head) # 12 46 | seq2.head = 2 # set back to 2 47 | seq2 << Noise() * Envelope(1) # 1 s of noise starts at the 2 second mark 48 | 49 | # You can accomplish the same thing with the insert function 50 | seq2.insert(Noise() * Envelope(1), 4) # 1 s of noise starts at the 4 second mark 51 | 52 | s.play_all(seq2) 53 | sleep(seq2.length) # still 12 s long because the inserted noise didn't extend its length -------------------------------------------------------------------------------- /include/Tact/Detail/Operator.inl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tact 4 | { 5 | 6 | inline Signal operator+(Signal lhs, Signal rhs) 7 | { 8 | return Sum(std::move(lhs), std::move(rhs)); 9 | } 10 | 11 | inline Signal operator+(double lhs, Signal rhs) 12 | { 13 | rhs.bias += lhs; 14 | return rhs; 15 | } 16 | 17 | inline Signal operator+(Signal lhs, double rhs) 18 | { 19 | lhs.bias += rhs; 20 | return lhs; 21 | } 22 | 23 | inline Signal &operator+=(Signal &lhs, double rhs) 24 | { 25 | lhs.bias += rhs; 26 | return lhs; 27 | } 28 | 29 | inline Signal operator-(Signal lhs, Signal rhs) 30 | { 31 | rhs *= -1; 32 | return Sum(std::move(lhs), std::move(rhs)); 33 | } 34 | 35 | inline Signal operator-(double lhs, Signal rhs) 36 | { 37 | return lhs + -1 * rhs; 38 | } 39 | 40 | inline Signal operator-(Signal lhs, double rhs) 41 | { 42 | lhs.bias -= rhs; 43 | return lhs; 44 | } 45 | 46 | inline Signal &operator-=(Signal &lhs, double rhs) 47 | { 48 | lhs.bias -= rhs; 49 | return lhs; 50 | } 51 | 52 | inline Signal operator-(Signal lhs) 53 | { 54 | return -1 * lhs; 55 | } 56 | 57 | inline Signal operator*(Signal lhs, Signal rhs) 58 | { 59 | return Product(std::move(lhs), std::move(rhs)); 60 | } 61 | 62 | inline Signal operator*(double lhs, Signal rhs) 63 | { 64 | rhs.gain *= lhs; 65 | rhs.bias *= lhs; 66 | return rhs; 67 | } 68 | 69 | inline Signal operator*(Signal lhs, double rhs) 70 | { 71 | lhs.gain *= rhs; 72 | lhs.bias *= rhs; 73 | return lhs; 74 | } 75 | 76 | inline Signal &operator*=(Signal &lhs, double rhs) 77 | { 78 | lhs.gain *= rhs; 79 | lhs.bias *= rhs; 80 | return lhs; 81 | } 82 | 83 | } // namespace tact -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!126 &1 4 | NavMeshProjectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | areas: 8 | - name: Walkable 9 | cost: 1 10 | - name: Not Walkable 11 | cost: 1 12 | - name: Jump 13 | cost: 2 14 | - name: 15 | cost: 1 16 | - name: 17 | cost: 1 18 | - name: 19 | cost: 1 20 | - name: 21 | cost: 1 22 | - name: 23 | cost: 1 24 | - name: 25 | cost: 1 26 | - name: 27 | cost: 1 28 | - name: 29 | cost: 1 30 | - name: 31 | cost: 1 32 | - name: 33 | cost: 1 34 | - name: 35 | cost: 1 36 | - name: 37 | cost: 1 38 | - name: 39 | cost: 1 40 | - name: 41 | cost: 1 42 | - name: 43 | cost: 1 44 | - name: 45 | cost: 1 46 | - name: 47 | cost: 1 48 | - name: 49 | cost: 1 50 | - name: 51 | cost: 1 52 | - name: 53 | cost: 1 54 | - name: 55 | cost: 1 56 | - name: 57 | cost: 1 58 | - name: 59 | cost: 1 60 | - name: 61 | cost: 1 62 | - name: 63 | cost: 1 64 | - name: 65 | cost: 1 66 | - name: 67 | cost: 1 68 | - name: 69 | cost: 1 70 | - name: 71 | cost: 1 72 | m_LastAgentTypeID: -887442657 73 | m_Settings: 74 | - serializedVersion: 2 75 | agentTypeID: 0 76 | agentRadius: 0.5 77 | agentHeight: 2 78 | agentSlope: 45 79 | agentClimb: 0.75 80 | ledgeDropHeight: 0 81 | maxJumpAcrossDistance: 0 82 | minRegionArea: 2 83 | manualCellSize: 0 84 | cellSize: 0.16666667 85 | manualTileSize: 0 86 | tileSize: 256 87 | accuratePlacement: 0 88 | debug: 89 | m_Flags: 0 90 | m_SettingNames: 91 | - Humanoid 92 | -------------------------------------------------------------------------------- /include/Tact/Error.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | /// Return errors 28 | enum SyntactsError : int { 29 | SyntactsError_NoError = 0, 30 | SyntactsError_InvalidDevice = -1, 31 | SyntactsError_NotOpen = -2, 32 | SyntactsError_AlreadyOpen = -3, 33 | SyntactsError_InvalidChannel = -4, 34 | SyntactsError_InvalidChannelCount = -5, 35 | SyntactsError_InvalidSampleRate = -6, 36 | SyntactsError_NoWaveform = -7, 37 | SyntactsError_ControlPanelFail = -8, 38 | SyntactsError_InvalidAPI = -9 39 | }; -------------------------------------------------------------------------------- /src/Tact/MemoryPool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace tact { 6 | 7 | HeapPool::HeapPool(std::size_t blockSize, std::size_t numBlocks) : 8 | m_blockSize(blockSize), 9 | m_numBlocks(numBlocks), 10 | m_blocksUsed(0) 11 | { 12 | assert(blockSize >= 8 && "Block size must be greater or equal to 8"); 13 | m_memory = std::malloc(blockSize * numBlocks); 14 | reset(); 15 | std::cout << "HeapPool Created" << std::endl; 16 | }; 17 | 18 | HeapPool::~HeapPool() 19 | { 20 | std::free(m_memory); 21 | std::cout << "HeapPool Destroyed" << std::endl; 22 | } 23 | 24 | void* HeapPool::allocate() 25 | { 26 | std::cout << "allocate" << std::endl; 27 | Block *freePosition = pop(); 28 | assert(freePosition != nullptr && "The pool PoolAllocator is full"); 29 | m_blocksUsed++; 30 | return (void *)freePosition; 31 | } 32 | 33 | void HeapPool::deallocate(void *ptr) 34 | { 35 | std::cout << "deallocate" << std::endl; 36 | m_blocksUsed--; 37 | push((Block *)ptr); 38 | } 39 | 40 | void HeapPool::reset() 41 | { 42 | m_blocksUsed = 0; 43 | for (std::size_t i = 0; i < m_numBlocks; ++i) 44 | { 45 | std::size_t address = (std::size_t)m_memory + i * m_blockSize; 46 | push((Block *)address); 47 | } 48 | } 49 | 50 | std::size_t HeapPool::blocksTotal() const { 51 | return m_numBlocks; 52 | } 53 | 54 | std::size_t HeapPool::blocksUsed() const { 55 | return m_blocksUsed; 56 | } 57 | 58 | std::size_t HeapPool::blocksAvail() const { 59 | return m_numBlocks - m_blocksUsed; 60 | } 61 | 62 | 63 | void HeapPool::push(Block *newBlock) 64 | { 65 | newBlock->next = m_head; 66 | m_head = newBlock; 67 | } 68 | 69 | HeapPool::Block* HeapPool::pop() 70 | { 71 | Block *top = m_head; 72 | m_head = m_head->next; 73 | return top; 74 | } 75 | 76 | } // namespace tact -------------------------------------------------------------------------------- /examples/example_sequences.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace tact; 5 | 6 | int main(int argc, char const *argv[]) 7 | { 8 | Session s; 9 | s.open(); 10 | 11 | // Sequences allow to you order multiple Signals in time. 12 | 13 | // Most simply, you can concatenate two or more signals: 14 | Signal sigA = Sine(440) * ASR(1,1,1); 15 | Signal sigB = Square(440) * ADSR(1,1,1,1); 16 | 17 | Sequence seq1 = sigA << sigB; 18 | 19 | std::cout << sigA.length() << std::endl; // 3 s 20 | std::cout << sigB.length() << std::endl; // 4 s 21 | std::cout << seq1.length() << std::endl; // 7 s 22 | 23 | s.playAll(seq1); 24 | sleep(seq1.length()); 25 | 26 | // You can also add delay, pause, and fade with scalar values: 27 | Sequence seq2 = 1 << sigA << 2 << sigB << -1 << sigA; // 1 s delay, 2 s pause, 1 s fade 28 | 29 | std::cout << seq2.length() << std::endl; // 12 s 30 | 31 | s.playAll(seq2); 32 | sleep(seq2.length()); 33 | 34 | // Of course, Sequences of Sequences is possible: 35 | Sequence seq3 = seq1 << seq2; // note this will also modify seq1 36 | std::cout << seq3.length() << std::endl; // 19, we won't play this one :) 37 | 38 | // The << operator inserts a Signal at the Sequence head position, 39 | // and then moves it forward or backward. You can get/set the head position 40 | // manually for the next << operation: 41 | 42 | std::cout << seq2.head << std::endl; // 12 43 | seq2.head = 2; // set back to 2 44 | seq2 << Noise() * Envelope(1); // 1 s of noise starts at the 2 second mark 45 | 46 | // You can accomplish the same thing with the insert function 47 | seq2.insert(Noise() * Envelope(1), 4); // 1 s of noise starts at the 4 second mark 48 | 49 | s.playAll(seq2); 50 | sleep(seq2.length()); // still 12 s long because the inserted noise didn't extend its length 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /src/Tact/Process.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tact 4 | { 5 | 6 | Repeater::Repeater() : repetitions(1), 7 | delay(0) 8 | { 9 | } 10 | 11 | Repeater::Repeater(Signal _signal, int _repetitions, double _delay) : signal(std::move(_signal)), 12 | repetitions(_repetitions), 13 | delay(_delay) 14 | { 15 | } 16 | 17 | double Repeater::sample(double t) const 18 | { 19 | double sigLen = signal.length(); 20 | double intLen = sigLen + delay; 21 | double maxLen = sigLen * repetitions + delay * (repetitions - 1); 22 | if (t <= maxLen) 23 | { 24 | double s = std::fmod(t, intLen); 25 | if (s <= sigLen) 26 | { 27 | return signal.sample(s); 28 | } 29 | return 0; 30 | } 31 | return 0; 32 | } 33 | 34 | double Repeater::length() const 35 | { 36 | return signal.length() * repetitions + delay * (repetitions - 1); 37 | } 38 | 39 | Stretcher::Stretcher() : factor(1) {} 40 | 41 | Stretcher::Stretcher(Signal _signal, double _factor) : signal(_signal), 42 | factor(_factor) 43 | { 44 | } 45 | 46 | double Stretcher::sample(double t) const 47 | { 48 | return signal.sample(t / factor); 49 | } 50 | 51 | double Stretcher::length() const 52 | { 53 | return signal.length() * factor; 54 | } 55 | 56 | Reverser::Reverser() 57 | { 58 | } 59 | 60 | Reverser::Reverser(Signal _signal) : signal(std::move(_signal)) 61 | { 62 | } 63 | 64 | double Reverser::sample(double t) const 65 | { 66 | double l = signal.length(); 67 | l = l == INF ? 1000000000 : l; 68 | t = clamp(l - t, 0, 1000000000); 69 | return signal.sample(t); 70 | } 71 | 72 | double Reverser::length() const 73 | { 74 | return signal.length(); 75 | } 76 | 77 | } // namespace tact 78 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.2d.sprite": "1.0.0", 4 | "com.unity.2d.tilemap": "1.0.0", 5 | "com.unity.ads": "3.4.9", 6 | "com.unity.analytics": "3.3.5", 7 | "com.unity.collab-proxy": "1.2.16", 8 | "com.unity.ide.rider": "1.1.4", 9 | "com.unity.ide.vscode": "1.2.1", 10 | "com.unity.purchasing": "2.1.0", 11 | "com.unity.test-framework": "1.1.16", 12 | "com.unity.textmeshpro": "2.0.1", 13 | "com.unity.timeline": "1.2.6", 14 | "com.unity.ugui": "1.0.0", 15 | "com.unity.modules.ai": "1.0.0", 16 | "com.unity.modules.androidjni": "1.0.0", 17 | "com.unity.modules.animation": "1.0.0", 18 | "com.unity.modules.assetbundle": "1.0.0", 19 | "com.unity.modules.audio": "1.0.0", 20 | "com.unity.modules.cloth": "1.0.0", 21 | "com.unity.modules.director": "1.0.0", 22 | "com.unity.modules.imageconversion": "1.0.0", 23 | "com.unity.modules.imgui": "1.0.0", 24 | "com.unity.modules.jsonserialize": "1.0.0", 25 | "com.unity.modules.particlesystem": "1.0.0", 26 | "com.unity.modules.physics": "1.0.0", 27 | "com.unity.modules.physics2d": "1.0.0", 28 | "com.unity.modules.screencapture": "1.0.0", 29 | "com.unity.modules.terrain": "1.0.0", 30 | "com.unity.modules.terrainphysics": "1.0.0", 31 | "com.unity.modules.tilemap": "1.0.0", 32 | "com.unity.modules.ui": "1.0.0", 33 | "com.unity.modules.uielements": "1.0.0", 34 | "com.unity.modules.umbra": "1.0.0", 35 | "com.unity.modules.unityanalytics": "1.0.0", 36 | "com.unity.modules.unitywebrequest": "1.0.0", 37 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0", 38 | "com.unity.modules.unitywebrequestaudio": "1.0.0", 39 | "com.unity.modules.unitywebrequesttexture": "1.0.0", 40 | "com.unity.modules.unitywebrequestwww": "1.0.0", 41 | "com.unity.modules.vehicles": "1.0.0", 42 | "com.unity.modules.video": "1.0.0", 43 | "com.unity.modules.vr": "1.0.0", 44 | "com.unity.modules.wind": "1.0.0", 45 | "com.unity.modules.xr": "1.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /csharp/examples/example_signals/example_signals.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Syntacts; 4 | 5 | // run with the following console command: 6 | // dotnet run 7 | 8 | class Example 9 | { 10 | static void Main(string[] args) 11 | { 12 | Session session = new Session(); 13 | session.Open(); 14 | 15 | int freq = 440; //Hz, Choose signal frequency, 440 Hz is audible and ok for speakers. Many tactors are closer to 150-250 Hz 16 | 17 | // We will begin by creating some basic oscillators, these are default amplitude 1.0 and infinite length of time 18 | Signal sin = new Sine(freq); // Sine wave 19 | Signal squ = new Square(freq); // Square wave 20 | Signal saw = new Saw(freq); // Saw wave 21 | Signal tri = new Triangle(freq); // Triangle wave 22 | // We can use pulse width modulation (PWM) to quickly create a repeating cue train with frequency (1Hz) and duty cycle (0.3) 23 | Signal pwm = new Pwm(1, 0.3); 24 | 25 | // Now we can pair those oscillators with an envelope to give them shape 26 | 27 | // This is a basic envelope that specifies amplitude (0.9), and duration (0.5 sec) 28 | Signal bas = new Envelope(0.9, 0.5); 29 | // This is an attack (1 sec), sustain (3 sec), release (1 sec) envelope. The sustain amplitude is 1.0. 30 | Signal asr = new ASR(1, 2, 1, 1.0); 31 | // This adds one more part to the above envelope. Attack (1 sec, to amplitude 1.0), decay (2 sec), 32 | // sustain (3 sec, amplitude 0.8), release (1 sec). Curves can be added here as well 33 | Signal adsr = new ADSR(1, 2, 3, 1, 1.0, 0.8); 34 | 35 | 36 | // Pairing these oscillators and envelopes can give us complex cues 37 | 38 | Signal sig1 = sin * bas; 39 | 40 | Signal sig2 = sin * pwm * adsr; 41 | 42 | // More information in sequencing these in time can be found in examples_sequences 43 | session.Play(0,sig2); 44 | Sleep(sig2.length); 45 | session.Stop(0); 46 | 47 | session.Dispose(); 48 | } 49 | static void Sleep(double seconds) { 50 | Thread.Sleep((int)(seconds*1000)); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /csharp/examples/example_sequences/example_sequences.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Syntacts; 4 | 5 | class example 6 | { 7 | static void Main(string[] args) 8 | { 9 | Session s = new Session(); 10 | s.Open(); 11 | 12 | // Sequences allow to you order multiple Signals in time. 13 | 14 | // Most simply, you can concatenate two or more signals: 15 | Signal sigA = new Sine(440) * new ASR(1,1,1); 16 | Signal sigB = new Square(440) * new ADSR(1,1,1,1); 17 | 18 | Sequence seq1 = new Sequence(); 19 | seq1.Push(sigA).Push(sigB); 20 | 21 | Console.WriteLine(sigA.length); // 3 s 22 | Console.WriteLine(sigB.length); // 4 s 23 | Console.WriteLine(seq1.length); // 7 s 24 | 25 | s.PlayAll(seq1); 26 | Sleep(seq1.length); 27 | 28 | // You can also add delay, pause, and fade with scalar values: 29 | Sequence seq2 = new Sequence(); 30 | seq2.Push(1).Push(sigA).Push(2).Push(sigB).Push(-1).Push(sigA); // 1 s delay, 2 s pause, 1 s fade 31 | 32 | Console.WriteLine(seq2.length); //12 s 33 | 34 | s.PlayAll(seq2); 35 | Sleep(seq2.length); 36 | 37 | // Of course, Sequences of Sequences is possible: 38 | Sequence seq3 = new Sequence(); 39 | seq3.Push(seq1).Push(seq2); // note this will also modify seq1 40 | Console.WriteLine(seq3.length); // 19, we won't play this one :) 41 | 42 | // The << operator inserts a Signal at the Sequence head position, 43 | // and then moves it forward or backward. You can get/set the head position 44 | // manually for the next << operation: 45 | 46 | Console.WriteLine(seq2.head); // 12 47 | seq2.head = 2; // set back to 2 48 | seq2.Push(new Noise() * new Envelope(1)); // 1 s of noise starts at the 2 second mark 49 | 50 | // You can accomplish the same thing with the insert function 51 | seq2.Insert(new Noise() * new Envelope(1), 4); // 1 s of noise starts at the 4 second mark 52 | 53 | s.PlayAll(seq2); 54 | Sleep(seq2.length); // still 12 s long because the inserted noise didn't extend its length 55 | 56 | s.Dispose(); 57 | } 58 | static void Sleep(double seconds) { 59 | Thread.Sleep((int)(seconds*1000)); 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /tests/benchmark.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace tact; 6 | 7 | void display(double t, int n, float sum, const std::string& benchmark) { 8 | std::cout << std::endl; 9 | std::cout << " Benchmark: " << benchmark << std::endl; 10 | std::cout << " Time: " << t << " s" << std::endl; 11 | std::cout << " Frequency: " << n / t / 1000 << " kHz" << std::endl; 12 | std::cout << " Channels: " << (n / t) / 48000 << std::endl; 13 | std::cout << " Sum: " << sum << std::endl; 14 | } 15 | 16 | int main(int argc, char const *argv[]) 17 | { 18 | int n = 100000000; // one billion benchmark iterations 19 | volatile float sum = 0; // benchmark accumulator to trick compiler 20 | 21 | int bufferSize = 32; 22 | std::vector sBuffer(bufferSize); 23 | std::vector tBuffer(bufferSize); 24 | 25 | auto env = Envelope(); 26 | 27 | Signal sig = Sine(2 * PI * 175 * Time() + 2 * Sine(10)) * env; 28 | tic(); 29 | float lenN = sig.length() / n; 30 | for (int i = 0; i < n; ++i) { 31 | auto t = i * lenN; 32 | sum += sig.sample(t); 33 | } 34 | display(toc(), n, sum, "Manual"); 35 | 36 | sig = Sine(175, Sine(10),2) * env; 37 | sum = 0; 38 | tic(); 39 | for (int i = 0; i < n; ++i) { 40 | auto t = i * lenN; 41 | sum += sig.sample(t); 42 | } 43 | display(toc(), n, sum, "Auto"); 44 | 45 | sig = Expression("sin(2*pi*175*t+2*sin(2*pi*10*t))") * env; 46 | sum = 0; 47 | tic(); 48 | for (int i = 0; i < n; ++i) { 49 | auto t = i * lenN; 50 | sum += sig.sample(t); 51 | } 52 | display(toc(), n, sum, "Expression"); 53 | 54 | sum = 0; 55 | tic(); 56 | for (int i = 0; i < n; ++i) { 57 | auto t = i * lenN; 58 | sum += std::sin(TWO_PI * 175 * t + 2 * std::sin(2.0f * PI * 10 * t)) * env.sample(t); 59 | } 60 | display(toc(), n, sum, "Best Case"); 61 | 62 | sum = 0; 63 | tic(); 64 | for (int i = 0; i < n; ++i) { 65 | sig = Sine(i) * Saw(i) * Envelope(5); 66 | auto t = i * lenN / n; 67 | sum += sig.sample(t); 68 | } 69 | display(toc(), n, sum, "Allocation"); 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!19 &1 4 | Physics2DSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 4 7 | m_Gravity: {x: 0, y: -9.81} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_VelocityIterations: 8 10 | m_PositionIterations: 3 11 | m_VelocityThreshold: 1 12 | m_MaxLinearCorrection: 0.2 13 | m_MaxAngularCorrection: 8 14 | m_MaxTranslationSpeed: 100 15 | m_MaxRotationSpeed: 360 16 | m_BaumgarteScale: 0.2 17 | m_BaumgarteTimeOfImpactScale: 0.75 18 | m_TimeToSleep: 0.5 19 | m_LinearSleepTolerance: 0.01 20 | m_AngularSleepTolerance: 2 21 | m_DefaultContactOffset: 0.01 22 | m_JobOptions: 23 | serializedVersion: 2 24 | useMultithreading: 0 25 | useConsistencySorting: 0 26 | m_InterpolationPosesPerJob: 100 27 | m_NewContactsPerJob: 30 28 | m_CollideContactsPerJob: 100 29 | m_ClearFlagsPerJob: 200 30 | m_ClearBodyForcesPerJob: 200 31 | m_SyncDiscreteFixturesPerJob: 50 32 | m_SyncContinuousFixturesPerJob: 50 33 | m_FindNearestContactsPerJob: 100 34 | m_UpdateTriggerContactsPerJob: 100 35 | m_IslandSolverCostThreshold: 100 36 | m_IslandSolverBodyCostScale: 1 37 | m_IslandSolverContactCostScale: 10 38 | m_IslandSolverJointCostScale: 10 39 | m_IslandSolverBodiesPerJob: 50 40 | m_IslandSolverContactsPerJob: 50 41 | m_AutoSimulation: 1 42 | m_QueriesHitTriggers: 1 43 | m_QueriesStartInColliders: 1 44 | m_CallbacksOnDisable: 1 45 | m_ReuseCollisionCallbacks: 1 46 | m_AutoSyncTransforms: 0 47 | m_AlwaysShowColliders: 0 48 | m_ShowColliderSleep: 1 49 | m_ShowColliderContacts: 0 50 | m_ShowColliderAABB: 0 51 | m_ContactArrowScale: 0.2 52 | m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} 53 | m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} 54 | m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} 55 | m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} 56 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 57 | -------------------------------------------------------------------------------- /include/Tact/Serialization.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | /////////////////////////////////////////////////////////////////////////////// 34 | 35 | #define TACT_SERIALIZABLE friend class cereal::access; \ 36 | template \ 37 | void serialize(Archive& archive) {} 38 | 39 | #define TACT_SERIALIZE(...) friend class cereal::access; \ 40 | template \ 41 | void serialize(Archive& archive) { \ 42 | archive(__VA_ARGS__); \ 43 | } 44 | 45 | #define TACT_PARENT(T) ::cereal::make_nvp(#T, cereal::base_class(this)) 46 | 47 | #define TACT_MEMBER(T) ::cereal::make_nvp(#T, T) 48 | 49 | /////////////////////////////////////////////////////////////////////////////// 50 | -------------------------------------------------------------------------------- /python/example_toh.py: -------------------------------------------------------------------------------- 1 | from syntacts import * 2 | from time import sleep 3 | import math 4 | from math import cos 5 | from math import pi 6 | 7 | # This example is taken directly from the code listings in: 8 | # Syntacts: Open-Source Software and Hardwarefor Audio-Controlled Haptics, Transactions on Haptics 9 | 10 | ## LISTING 1 11 | 12 | # create an audio context 13 | session = Session() 14 | 15 | # enumerate connected hardware 16 | for dev in session.available_devices: 17 | print(dev.index) # e.g., 6 18 | print(dev.name) # e.g., MOTU Pro Audio 19 | print(dev.max_channels) # e.g., 24 20 | print(dev.api_name) # e.g., ASIO 21 | 22 | # open device 6 with 24 channels at 48 kHz 23 | session.open(22, 24, 48000) 24 | sleep(1) 25 | 26 | ## LISTING 2 27 | 28 | sqr = Square(100) # 100 Hz square 29 | sin = Sine(10) # 10 Hz triangle 30 | asr = ASR(0.1,0.1,0.1) # attack, sustain, release 31 | # basic examples mixing the Signals above 32 | sig1 = sqr * sin 33 | sig2 = sig1 * asr 34 | sig3 = 0.5 * (sqr + sin) * asr 35 | # play Signals on channel 0 and 1 36 | session.play(0, sig1) # plays until stopped 37 | session.play(1, sig2) # plays for 0.3 seconds 38 | sleep(1) 39 | session.stop(0) # stop sig1 40 | 41 | ## LISTING 3 42 | 43 | sigA = Sine(100) * ASR(0.1,0.1,0.1) # 0.3 s 44 | sigB = Sine(50) * ADSR(0.1,0.1,0.1,0.1) # 0.4 s 45 | sig4 = sigA << sigB # 0.7 s 46 | sig5 = 0.1 << sigA << 0.2 << sigB # 1.0 s 47 | sig6 = sigA << -0.1 << sigB # 0.6 s 48 | sig7 = sig4 << sig5 << sig6 # 2.3 s 49 | session.play(2,sig7) 50 | 51 | sleep(3) 52 | 53 | ## LISTING 4 54 | 55 | spatial = Spatializer(session) # 2D Spatializer 56 | spatial.create_grid(4,6) # 4 rows X 6 cols 57 | spatial.set_position(18,(0.1,0.8)) # move channel 18 58 | spatial.roll_off = 'linear' # roll-off law 59 | spatial.radius = 0.3 # effect radius 60 | spatial.target = (0.2, 0.1) # target location 61 | spatial.play(sig1) # play inf Signal 62 | 63 | # modification in a loop 64 | t = 0 65 | while (t < 10): 66 | x = 0.5 + 0.5 * math.cos(2*pi*t) 67 | y = 0.5 + 0.5 * math.sin(2*pi*t) 68 | spatial.target = (x, y) 69 | spatial.volume = 0.5 + 0.5 * math.sin(2*pi*t) 70 | spatial.pitch = 1 + 0.5 * math.sin(2*pi*t) 71 | sleep(0.01) 72 | t = t + 0.01 73 | 74 | spatial.stop() 75 | 76 | del spatial -------------------------------------------------------------------------------- /examples/example_signals.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace tact; // everything in Syntacts is under the namespace tact 4 | 5 | int main(int argc, char const *argv[]) 6 | { 7 | Session session; 8 | session.open(); 9 | 10 | int freq = 440; //Hz, Choose signal frequency, 440 Hz is audible and ok for speakers. Many tactors are closer to 150-250 Hz 11 | 12 | // We will begin by creating some basic oscillators, these are default amplitude 1.0 and infinite length of time 13 | Signal sin = Sine(freq); // Sine wave 14 | Signal squ = Square(freq); // Square wave 15 | Signal saw = Saw(freq); // Saw wave 16 | Signal tri = Triangle(freq); // Triangle wave 17 | // We can use pulse width modulation (PWM) to quickly create a repeating cue train with frequency (1Hz) and duty cycle (0.3) 18 | Signal pwm = Pwm(1, 0.3); 19 | 20 | // Now we can pair those oscillators with an envelope to give them shape 21 | 22 | // This is a basic envelope that specifies duration (0.9 sec), and amplitude (0.5) 23 | Signal bas = Envelope(0.9, 0.5); 24 | // This is an attack (1 sec), sustain (3 sec), release (1 sec) envelope. The sustain amplitude is 1.0. 25 | // Envelopes can interpolate between amplitudes with different curves, this example uses a smooth s-curve and linear. 26 | Signal asr = ASR(1, 2, 1, 1.0, Curves::Smootheststep(), Curves::Linear()); 27 | // This adds one more part to the above envelope. Attack (1 sec, to amplitude 1.0), decay (2 sec), 28 | // sustain (3 sec, amplitude 0.8), release (1 sec). Curves can be added here as well 29 | Signal adsr = ADSR(1, 2, 3, 1, 1.0, 0.8); 30 | // You can also use another oscillator as an envelope. This is a 10 Hz sine wave, duration 2.1 sec, amplitude 0.9 31 | Signal sigEnv = SignalEnvelope(Sine(10), 2.1, 0.9); 32 | 33 | 34 | // Pairing these oscillators and envelopes can give us complex cues 35 | 36 | Signal sig1 = sin * bas; 37 | 38 | Signal sig2 = sin * pwm * adsr; 39 | 40 | // You can also do addition/subtraction operations to create cues you may wany 41 | 42 | Signal sig3 = squ - pwm; 43 | 44 | // More information in sequencing these in time can be found in examples_sequences 45 | session.play(0,sig2); 46 | sleep(sig2.length()); 47 | session.stop(0); 48 | 49 | session.close(); 50 | } 51 | -------------------------------------------------------------------------------- /python/example_library.py: -------------------------------------------------------------------------------- 1 | from syntacts import * 2 | from time import sleep 3 | from math import sin 4 | from math import pi 5 | 6 | #----------------------------------------------------------- 7 | # Function to make sure export/import works 8 | def check(signal): 9 | if signal is not None: 10 | print('Pass') 11 | else: 12 | print('Fail') 13 | 14 | #----------------------------------------------------------- 15 | s = Session() 16 | s.open() 17 | 18 | # Make a Signal to save/export 19 | py = Sine(440) * ASR(1,1,1) 20 | 21 | # Syntacts Binary Format (Default Location, i.e. APPDATA/Syntacts/Library) 22 | 23 | Library.save_signal(py, 'py') 24 | loaded = Library.load_signal('py') 25 | s.play(0,loaded) 26 | sleep(loaded.length) 27 | check(loaded) 28 | 29 | # # Syntacts Binary Format (Custom Location) 30 | 31 | Library.export_signal(py, 'relative/folder/py.sig') 32 | loaded = Library.import_signal('relative/folder/py.sig') 33 | check(loaded) 34 | del loaded 35 | 36 | Library.export_signal(py, '/absolute/folder/py.sig') 37 | loaded = Library.import_signal('/absolute/folder/py.sig') 38 | check(loaded) 39 | del loaded 40 | 41 | # # Syntacts JSON Format 42 | 43 | Library.export_signal(py, 'relative/folder/py.json') 44 | loaded = Library.import_signal('relative/folder/py.json') 45 | check(loaded) 46 | del loaded 47 | 48 | Library.export_signal(py, '/absolute/folder/py.json') 49 | loaded = Library.import_signal('/absolute/folder/py.json') 50 | check(loaded) 51 | del loaded 52 | 53 | # # WAV Format 54 | 55 | Library.export_signal(py, 'relative/folder/py.wav') 56 | loaded = Library.import_signal('relative/folder/py.wav') 57 | check(loaded) 58 | del loaded 59 | 60 | Library.export_signal(py, '/absolute/folder/py.wav') 61 | loaded = Library.import_signal('/absolute/folder/py.wav') 62 | check(loaded) 63 | del loaded 64 | 65 | # # AIFF Format 66 | 67 | Library.export_signal(py, 'relative/folder/py.aiff') 68 | loaded = Library.import_signal('relative/folder/py.aiff') 69 | check(loaded) 70 | del loaded 71 | 72 | Library.export_signal(py, '/absolute/folder/py.aiff') 73 | loaded = Library.import_signal('/absolute/folder/py.aiff') 74 | check(loaded) 75 | del loaded 76 | 77 | # CSV/TXT Format (import not yet supported) 78 | 79 | Library.export_signal(py, 'py.csv') 80 | Library.export_signal(py, 'relative/folder/py.txt') 81 | Library.export_signal(py, '/absolute/folder/py.txt') 82 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Demo/BouncyBall.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: BouncyBall 11 | m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} 12 | m_ShaderKeywords: 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _MainTex: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MetallicGlossMap: 47 | m_Texture: {fileID: 0} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _OcclusionMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _ParallaxMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | m_Floats: 59 | - _BumpScale: 1 60 | - _Cutoff: 0.5 61 | - _DetailNormalMapScale: 1 62 | - _DstBlend: 0 63 | - _GlossMapScale: 1 64 | - _Glossiness: 0.5 65 | - _GlossyReflections: 1 66 | - _Metallic: 0 67 | - _Mode: 0 68 | - _OcclusionStrength: 1 69 | - _Parallax: 0.02 70 | - _SmoothnessTextureChannel: 0 71 | - _SpecularHighlights: 1 72 | - _SrcBlend: 1 73 | - _UVSec: 0 74 | - _ZWrite: 1 75 | m_Colors: 76 | - _Color: {r: 0.94292456, g: 0.4292453, b: 1, a: 1} 77 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 78 | -------------------------------------------------------------------------------- /gui/src/FileWatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "FileWatcher.hpp" 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | FileWatcher::FileWatcher(std::string watchPath, int delay) : 13 | m_watchPath{watchPath}, 14 | m_delay{std::chrono::milliseconds(delay)}, 15 | m_running(false) 16 | { 17 | for (auto &file : std::filesystem::recursive_directory_iterator(m_watchPath)) 18 | { 19 | m_paths[file.path().string()] = std::filesystem::last_write_time(file); 20 | } 21 | } 22 | 23 | void FileWatcher::start(const std::function &action) 24 | { 25 | m_running = true; 26 | while (m_running) 27 | { 28 | // Wait for "delay" milliseconds 29 | std::this_thread::sleep_for(m_delay); 30 | 31 | auto it = m_paths.begin(); 32 | while (it != m_paths.end()) 33 | { 34 | if (!std::filesystem::exists(it->first)) 35 | { 36 | action(it->first, FileStatus::Erased); 37 | it = m_paths.erase(it); 38 | } 39 | else 40 | { 41 | it++; 42 | } 43 | } 44 | 45 | // Check if a file was created or modified 46 | for (auto &file : std::filesystem::recursive_directory_iterator(m_watchPath)) 47 | { 48 | auto current_file_last_write_time = std::filesystem::last_write_time(file); 49 | 50 | // File creation 51 | if (!contains(file.path().string())) 52 | { 53 | m_paths[file.path().string()] = current_file_last_write_time; 54 | action(file.path().string(), FileStatus::Created); 55 | // File modification 56 | } 57 | else 58 | { 59 | if (m_paths[file.path().string()] != current_file_last_write_time) 60 | { 61 | m_paths[file.path().string()] = current_file_last_write_time; 62 | action(file.path().string(), FileStatus::Modified); 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | void FileWatcher::stop() { 70 | m_running = false; 71 | } 72 | 73 | bool FileWatcher::contains(const std::string &key) 74 | { 75 | auto el = m_paths.find(key); 76 | return el != m_paths.end(); 77 | } 78 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!30 &1 4 | GraphicsSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 12 7 | m_Deferred: 8 | m_Mode: 1 9 | m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} 10 | m_DeferredReflections: 11 | m_Mode: 1 12 | m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} 13 | m_ScreenSpaceShadows: 14 | m_Mode: 1 15 | m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} 16 | m_LegacyDeferred: 17 | m_Mode: 1 18 | m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} 19 | m_DepthNormals: 20 | m_Mode: 1 21 | m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} 22 | m_MotionVectors: 23 | m_Mode: 1 24 | m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} 25 | m_LightHalo: 26 | m_Mode: 1 27 | m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} 28 | m_LensFlare: 29 | m_Mode: 1 30 | m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} 31 | m_AlwaysIncludedShaders: 32 | - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} 33 | - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} 34 | - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} 35 | - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} 36 | - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} 37 | - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} 38 | m_PreloadedShaders: [] 39 | m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, 40 | type: 0} 41 | m_CustomRenderPipeline: {fileID: 0} 42 | m_TransparencySortMode: 0 43 | m_TransparencySortAxis: {x: 0, y: 0, z: 1} 44 | m_DefaultRenderingPath: 1 45 | m_DefaultMobileRenderingPath: 1 46 | m_TierSettings: [] 47 | m_LightmapStripping: 0 48 | m_FogStripping: 0 49 | m_InstancingStripping: 0 50 | m_LightmapKeepPlain: 1 51 | m_LightmapKeepDirCombined: 1 52 | m_LightmapKeepDynamicPlain: 1 53 | m_LightmapKeepDynamicDirCombined: 1 54 | m_LightmapKeepShadowMask: 1 55 | m_LightmapKeepSubtractive: 1 56 | m_FogKeepLinear: 1 57 | m_FogKeepExp: 1 58 | m_FogKeepExp2: 1 59 | m_AlbedoSwatchInfos: [] 60 | m_LightsUseLinearIntensity: 0 61 | m_LightsUseColorTemperature: 0 62 | -------------------------------------------------------------------------------- /gui/src/Custom.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace ImGui { 9 | 10 | /////////////////////////////////////////////////////////////////////////////// 11 | 12 | // bool MiniSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, bool top, float power); 13 | // bool MiniSliderFloat(const char* label, float* v, float v_min, float v_max, bool top = true, float power = 1.0f); 14 | 15 | void RenderGrid(ImRect bb, int nx, int ny, ImU32 gridColor, ImU32 bgColor, float thickness = 1.0f, float rounding = 0); 16 | void PlotSignal(const char* label, const tact::Signal& sig, std::vector& points, float t1, float t2, ImVec4 color, float thickness, ImVec2 size = ImVec2(-1,0), bool grid = true, bool text = true); 17 | void RenderSignalInBounds(ImDrawList* DrawList, const tact::Signal& sig, float t1, float t2, ImRect bb, ImVec4 color, float thickness, int n = 0); 18 | 19 | 20 | /////////////////////////////////////////////////////////////////////////////// 21 | 22 | bool TimelineScrollbar(float* ltime, float* rtime, bool* lrgrabbed, bool* rgrabbed, bool* cgrabbed, ImVec2 size = ImVec2(-1, 0), float grabWidth = 10); 23 | 24 | /////////////////////////////////////////////////////////////////////////////// 25 | 26 | inline bool TintedButton(const char* label, ImVec4 color, const ImVec2& size = ImVec2(0,0)) { 27 | ImGui::PushStyleColor(ImGuiCol_Button, color); 28 | bool ret = ImGui::Button(label, size); 29 | ImGui::PopStyleColor(); 30 | return ret; 31 | } 32 | bool BeginHelpPopup(const char* name); 33 | 34 | /////////////////////////////////////////////////////////////////////////////// 35 | 36 | inline double NiceNum(double x, bool round) 37 | { 38 | int expv; /* exponent of x */ 39 | double f; /* fractional part of x */ 40 | double nf; /* nice, rounded fraction */ 41 | expv = floor(log10(x)); 42 | f = x / std::pow(10, expv); /* between 1 and 10 */ 43 | if (round) 44 | if (f < 1.5) 45 | nf = 1; 46 | else if (f < 3) 47 | nf = 2; 48 | else if (f < 7) 49 | nf = 5; 50 | else 51 | nf = 10; 52 | else if (f <= 1) 53 | nf = 1; 54 | else if (f <= 2) 55 | nf = 2; 56 | else if (f <= 5) 57 | nf = 5; 58 | else 59 | nf = 10; 60 | return nf * std::pow(10, expv); 61 | } 62 | 63 | } // namespace ImGui -------------------------------------------------------------------------------- /examples/example_music.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace tact; 9 | 10 | // This example is meant to show the full process of creating cues, sequencing 11 | // them in time, and finally playing it. We're not saying that Syntacts is meant 12 | // to create music, but this is an illustration that can be played over speakers 13 | 14 | /// Returns musical note n semitones away from f0 (440 Hz = A4 by default) 15 | template 16 | Signal note(int n, float f0 = 440) { 17 | static float a = std::pow(2.0f, 1.0f/12.0f); 18 | float f = f0 * std::pow(a,n); 19 | return Osc(f) * ASR(0.05f, 0.10f, 0.1f); 20 | } 21 | 22 | /// Returns musical note corresponding to name and octave 23 | template 24 | Signal key(const std::string& name, int octave) { 25 | static std::vector names = {"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"}; 26 | int idx = (int)std::distance(names.begin(), std::find(names.begin(), names.end(), name)); 27 | int i = idx - 9 + (octave - 4) * 12; 28 | return note(i, 440); 29 | } 30 | 31 | // Cretes the notes required for "Funkytown" and returns the notes sequenced in time 32 | Signal funktown() { 33 | auto Dsharp = key("D#",6); 34 | auto Csharp = key("C#",6); 35 | auto Asharp = key("A#",5); 36 | auto Gsharp = key("G#",6); 37 | auto G = key("G", 6); 38 | return Dsharp << Dsharp << Csharp << Dsharp << 0.2f << Asharp << 0.2f << Asharp << Dsharp << Gsharp << G << Dsharp; 39 | } 40 | 41 | // Cretes the notes required for "Dixie" and returns the notes sequenced in time 42 | Signal dixie() { 43 | return key("B", 5) << 44 | key("G#",5) << 45 | key("E", 5) << 46 | key("E", 5) << 47 | key("E", 5) << 48 | key("F#",5) << 49 | key("G#",5) << 50 | key("A", 5) << 51 | key("B", 5) << 52 | key("B", 5) << 53 | key("B", 5) << 54 | key("G#",5); 55 | } 56 | 57 | int main(int argc, char const *argv[]) 58 | { 59 | Session session; 60 | session.open(); 61 | auto f = funktown(); 62 | Library::saveSignal(f, "funky"); 63 | session.playAll(f); 64 | sleep(f.length() + 0.25f); 65 | auto d = dixie(); 66 | session.playAll(d); 67 | sleep(d.length() + 0.25f); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /python/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Signal files 132 | .aiff 133 | .json 134 | .sig 135 | .txt 136 | .wav 137 | -------------------------------------------------------------------------------- /csharp/examples/example_music/example_music.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Collections.Generic; 4 | using Syntacts; 5 | 6 | // run with the following console command: 7 | // dotnet run 8 | 9 | class Example 10 | { 11 | // This example is meant to show the full process of creating cues, sequencing 12 | // them in time, and finally playing it. We're not saying that Syntacts is meant 13 | // to create music, but this is an illustration that can be played over speakers 14 | 15 | /// Returns musical note n semitones away from f0 (440 Hz = A4 by default) 16 | static Signal key(string name, int octave){ 17 | string[] names = {"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"}; 18 | int idx = Array.IndexOf(names, name); 19 | float i = (float)idx - 9.0f + ((float)octave - 4.0f) * 12.0f; 20 | float a = (float)Math.Pow(2.0f,1.0f/12.0f); 21 | float freq = 440.0f * (float)Math.Pow(a,i); 22 | Console.WriteLine(freq); 23 | return new Square(freq) * new ASR(0.05, 0.10, 0.1); 24 | } 25 | // Cretes the notes required for "Funkytown" and returns the notes sequenced in time 26 | static Sequence funkytown() { 27 | Signal Dsharp = key("D#",6); 28 | Signal Csharp = key("C#",6); 29 | Signal Asharp = key("A#",5); 30 | Signal Gsharp = key("G#",6); 31 | Signal G = key("G", 6); 32 | Sequence funkytown = new Sequence(); 33 | return funkytown.Push(Dsharp).Push(Dsharp).Push(Csharp).Push(Dsharp).Push(0.2f).Push(Asharp).Push(0.2f).Push(Asharp).Push(Dsharp).Push(Gsharp).Push(G).Push(Dsharp); 34 | } 35 | 36 | // Cretes the notes required for "Dixie" and returns the notes sequenced in time 37 | static Sequence dixie() { 38 | Sequence dixie = new Sequence(); 39 | return dixie.Push(key("B", 5)).Push(key("G#",5)).Push(key("E", 5)).Push(key("E", 5)).Push(key("E", 5)).Push(key("F#",5)).Push(key("G#",5)).Push(key("A", 5)).Push(key("B", 5)).Push(key("B", 5)).Push(key("B", 5)).Push(key("G#",5)); 40 | } 41 | 42 | static void Main(string[] args) 43 | { 44 | Session session = new Session(); 45 | 46 | session.Open(); 47 | Sequence f = funkytown(); 48 | Library.SaveSignal(f, "funky"); 49 | session.PlayAll(f); 50 | Sleep(f.length + 0.25f); 51 | Sequence d = dixie(); 52 | session.PlayAll(d); 53 | Sleep(d.length + 0.25f); 54 | session.Dispose(); // important! 55 | } 56 | 57 | static void Sleep(double seconds) { 58 | Thread.Sleep((int)(seconds*1000)); 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # API 2 | ## Must Do 3 | - ~~remove Cue in favor of pure signals and operations~~ 4 | - ~~add duration to Signal and operators~~ 5 | - ~~Sequence class~~ 6 | - ~~determine when cloning should happen -- when passed as a param, or only once passed to the session?~~ 7 | - ~~finish implementing clone facilities (envelope, deep clone pointers, etc)~~ 8 | - ~~Signal pool allocator~~ 9 | - ~~add option to save Cues to chosen location~~ 10 | - ~~move sum/prod machinery into signal itself (e.g. offset and scale factor) or template do template specializations~~ 11 | - ~~pop signals from channel when over~~ 12 | - ~~change oscillator ctors to do 2*pi*f scaling in Scalar~~ 13 | - ~~realtime channel pitch adjustment~~ 14 | - ~~spatializer class~~ 15 | - ~~change to bias/gain~~ 16 | - ~~import WAV files~~ 17 | - ~~grid mechanism in spatializers~~ 18 | - ~channel polyphony ~ 19 | - import CSV 20 | - import Macaron JSON 21 | - save/load spatializers 22 | 23 | ## Optimization 24 | - ~~consider using unique_ptr in Signal with a clone method~~ 25 | - ~~eliminate Tweens in favor of static bezier objects~~ 26 | - multi-time sample functions (all the way down) 27 | - use of std::map for KeyedEnvelope complicates GUI, consider vectors 28 | 29 | ## Nice to Have 30 | - ~~Repeater, Stretcher, Reverse signals~~ 31 | - real-time manipulation of *any* parameter (see WebAudio AudioParam for inspiration) 32 | - look into cereal's minimal load/save capabilities 33 | - additional variables in expression 34 | 35 | # GUI 36 | ## Must Do 37 | - ~~Abstracted signal nodes in Designer tab~~ 38 | - ~~fix Info dialog~~ 39 | - ~~dual sliders for pitch/vol~~ 40 | - ~~render selected library item~~ 41 | - ~~fix library delete~~ 42 | - ~~don't load all lib items automatically~~ 43 | - ~~pop-up visualizer~~ 44 | - ~~dark/light themes~~ 45 | - ~~refresh library~~ 46 | - ~~Sequencer tab~~ 47 | - ~~remove carnot dep~~ 48 | - ~~improve dnd interface~~ 49 | - ~~drag/drop help dialogs~~ 50 | - improved slot header 51 | - ~~KeyEnvelope~~ 52 | - ~~library signal busting~~ 53 | - improve play/pause/stop interface 54 | - library signal references 55 | - log file 56 | 57 | ## Nice to Have 58 | - ~~CPU usage meter~~ 59 | - ~~drag/drop lib to channel~~ 60 | - ~~select device from Details listing~~ 61 | - ~~custom plot renderer without decimation~~ 62 | - ~~faster startup~~ 63 | - ~~last use memory (e.g. boot with last used device)~~ 64 | - copy/rename library items 65 | - custom themes 66 | - ASIO control panel 67 | 68 | # Plugin DLL / External API 69 | ## Must Do 70 | - ~~expose all (or most important) C++ API functionality~~ 71 | - ~~spatializer~~ 72 | - determine if ASIO has an issue in C# (works fine in Unity, so unsure) 73 | 74 | # Distribution / Documentation 75 | - ~~embed PortAudio / ASIO / gui~~ 76 | - ~~update examples~~ 77 | - create installer with NSIS 78 | - document all classes/functions 79 | 80 | -------------------------------------------------------------------------------- /python/example_basic.py: -------------------------------------------------------------------------------- 1 | from syntacts import * 2 | from time import sleep 3 | from math import sin 4 | from math import pi 5 | 6 | 7 | # ------------------------------------------------------------------------ 8 | 9 | # Syntacts usage begins with creating an audio context, or Session 10 | session = Session() 11 | 12 | # Now let's open a device ... 13 | 14 | # Usually, you would use "session.open(i)"" to open a specific device with index i 15 | 16 | # Alternatively, you can just open the system default device by passing no arguments 17 | # (which this example does because we don't know what device numbers you might have!) 18 | session.open() 19 | 20 | 21 | # ------------------------------------------------------------------------- 22 | 23 | # Now, let's create some vibrations ... 24 | 25 | # Vibrations are represented by Signals and combinations of Signals 26 | 27 | # Some Signals (e.g. oscillators) have an INFINITE duration 28 | sig1 = Sine(440) # a 440 Hz sinewave 29 | print(sig1.length) # inf 30 | 31 | # Other Signals (e.g. envelopes) have FINITE duration 32 | sig2 = ASR(1,3,1) # a 5 second attack, sustain, release envelope 33 | print(sig2.length) # 5 34 | 35 | # Signals can be combined using math operation 36 | sig3 = sig1 * sig2 # a 5 second 440 Hz sinewave with an ASR envelope 37 | print(sig3.length) # 5 38 | 39 | # Such operations can be done in a single line 40 | sig4 = Square(880) * Sine(10) * ADSR(1,1,1,1) # 880 Hz square, amplitude modulated with 10 Hz sine and 4 s ADSR envelope 41 | print(sig4.length) # 4 42 | 43 | # For more advanced Signal synthesis, see "example_signals.cpp" 44 | 45 | #------------------------------------------------------------------------- 46 | 47 | # Now that we have some Signals, let's play them ... 48 | 49 | # Play sig1 on channel 0 of the open Device 50 | session.play(0, sig1) 51 | # The Signal will immediately start playing in the Session's audio thread, 52 | # but we need to sleep this thread so that the program doesn't continue prematurely 53 | sleep(3) 54 | # Now, we stop the Signal on channel 0 (sig1 will have played for 3 seconds) 55 | session.stop(0) 56 | 57 | # Let's play another on channel 1... 58 | session.play(1, sig3) 59 | sleep(sig3.length) 60 | # We don't have to call session.stop(1) because sig3 is FINITE 61 | 62 | # You can also play a Signal on all channels 63 | session.play_all(sig4) 64 | sleep(sig4.length) 65 | 66 | #------------------------------------------------------------------------- 67 | 68 | # Devices will automatically close when the Session goes out of scope, 69 | # but it is good practice to do this explicitly 70 | 71 | session.close() 72 | 73 | #------------------------------------------------------------------------- 74 | 75 | # This was an extremely basic example of using Syntacts. See the other 76 | # examples for more complex usage and other included features! 77 | 78 | -------------------------------------------------------------------------------- /examples/example_library.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace tact; 5 | 6 | // Show pass/fail for testing purposes (you don't need this) 7 | inline void showResult(bool r) { 8 | std::cout << (r ? "Pass" : "Fail") << std::endl; 9 | } 10 | 11 | int main(int argc, char const *argv[]) 12 | { 13 | 14 | // Make a Signal to save/export, and a blank Signal to import in to 15 | Signal out = Sine(440) * ASR(1,1,1); 16 | Signal in; 17 | 18 | // Syntacts Binary Format (Default Location, i.e. APPDATA/Syntacts/Library) 19 | 20 | showResult( Library::saveSignal(out, "out")); 21 | showResult( Library::loadSignal(in, "out")); 22 | 23 | // Syntacts Binary Format (Custom Location) 24 | 25 | showResult( Library::exportSignal(out, "out.sig")); 26 | showResult( Library::importSignal(in, "out.sig")); 27 | 28 | showResult( Library::exportSignal(out, "relative/folder/out.sig")); 29 | showResult( Library::importSignal(in, "relative/folder/out.sig")); 30 | 31 | showResult( Library::exportSignal(out, "/absolute/folder/out.sig")); 32 | showResult( Library::importSignal(in, "/absolute/folder/out.sig")); 33 | 34 | // Syntacts JSON Format 35 | 36 | showResult( Library::exportSignal(out, "out.json")); 37 | showResult( Library::importSignal(in, "out.json")); 38 | 39 | showResult( Library::exportSignal(out, "relative/folder/out.json")); 40 | showResult( Library::importSignal(in, "relative/folder/out.json")); 41 | 42 | showResult( Library::exportSignal(out, "/absolute/folder/out.json")); 43 | showResult( Library::importSignal(in, "/absolute/folder/out.json")); 44 | 45 | // WAV Format 46 | 47 | showResult( Library::exportSignal(out, "out.wav")); 48 | showResult( Library::importSignal(in, "out.wav")); 49 | 50 | showResult( Library::exportSignal(out, "relative/folder/out.WAV")); 51 | showResult( Library::importSignal(in, "relative/folder/out.WAV")); 52 | 53 | showResult( Library::exportSignal(out, "/absolute/folder/out.wave")); 54 | showResult( Library::importSignal(in, "/absolute/folder/out.wave")); 55 | 56 | // AIFF Format 57 | 58 | showResult( Library::exportSignal(out, "out.aiff")); 59 | showResult( Library::importSignal(in, "out.aiff")); 60 | 61 | showResult( Library::exportSignal(out, "relative/folder/out.AIFF")); 62 | showResult( Library::importSignal(in, "relative/folder/out.AIFF")); 63 | 64 | showResult( Library::exportSignal(out, "/absolute/folder/out.aifc")); 65 | showResult( Library::importSignal(in, "/absolute/folder/out.aifc")); 66 | 67 | // CSV/TXT Format (import not yet supported) 68 | 69 | showResult( Library::exportSignal(out, "out.csv")); 70 | showResult( Library::exportSignal(out, "relative/folder/out.txt")); 71 | showResult( Library::exportSignal(out, "/absolute/folder/out.txt")); 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /src/Tact/Envelope.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace tact { 6 | 7 | Envelope::Envelope(double _duration, double _amplitude) : 8 | duration(_duration), 9 | amplitude(_amplitude) 10 | { } 11 | 12 | double Envelope::sample(double t) const { 13 | return t > duration ? 0.0f : amplitude; 14 | } 15 | 16 | double Envelope::length() const { 17 | return duration; 18 | } 19 | 20 | KeyedEnvelope::KeyedEnvelope(double amplitude0) 21 | { 22 | addKey(0.0f, amplitude0, Curves::Instant()); 23 | } 24 | 25 | void KeyedEnvelope::addKey(double t, double amplitude, Curve curve) { 26 | keys[t] = std::make_pair(amplitude, curve); 27 | } 28 | 29 | double KeyedEnvelope::sample(double t) const { 30 | if (t > length()) 31 | return 0.0f; 32 | auto b = keys.lower_bound(t); 33 | if (b->first == t) 34 | return b->second.first; 35 | auto a = std::prev(b); 36 | t = (t - a->first) / (b->first - a->first); 37 | double sample = b->second.second(a->second.first, b->second.first, t); 38 | return sample; 39 | } 40 | 41 | double KeyedEnvelope::length() const { 42 | return keys.rbegin()->first; 43 | } 44 | 45 | ASR::ASR(double attackTime, double sustainTime, double releaseTime, double attackAmplitude, Curve attackCurve, Curve releaseCurve) 46 | { 47 | addKey(attackTime, attackAmplitude, attackCurve); 48 | addKey(attackTime + sustainTime, attackAmplitude, Curves::Instant()); 49 | addKey(attackTime + sustainTime + releaseTime, 0.0f, releaseCurve); 50 | } 51 | 52 | 53 | ADSR::ADSR(double attackTime, double decayTime, double sustainTime, double releaseTime, double attackAmplitude, double decayAmplitude, Curve attackCurve, Curve decayCurve, Curve releaseCurve) 54 | { 55 | addKey(attackTime, attackAmplitude, attackCurve); 56 | addKey(attackTime + decayTime, decayAmplitude, decayCurve); 57 | addKey(attackTime + decayTime + sustainTime, decayAmplitude, Curves::Instant()); 58 | addKey(attackTime + decayTime + sustainTime + releaseTime, 0.0f, releaseCurve); 59 | } 60 | 61 | ExponentialDecay::ExponentialDecay(double _amplitude, double _decay) : amplitude(_amplitude), decay(_decay) { } 62 | 63 | double ExponentialDecay::sample(double t) const { 64 | return amplitude * std::exp(-decay * t); 65 | } 66 | 67 | double ExponentialDecay::length() const { 68 | return - std::log(0.001 /amplitude) / decay; 69 | } 70 | 71 | SignalEnvelope::SignalEnvelope(Signal _signal, double _duration , double _amplitude) : 72 | signal(_signal), duration(_duration), amplitude(_amplitude) 73 | { } 74 | 75 | double SignalEnvelope::sample(double t) const { 76 | if (t > length()) 77 | return 0.0f; 78 | double value = signal.sample(t); 79 | value = remap(value, -1, 1 , 0 , amplitude); 80 | return value; 81 | } 82 | 83 | double SignalEnvelope::length() const { 84 | return duration; 85 | } 86 | 87 | } // namespace tact -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts/Editor/logo.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f37ce78e955c1eb4cb8c25996487548b 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: -1 35 | aniso: -1 36 | mipBias: -100 37 | wrapU: -1 38 | wrapV: -1 39 | wrapW: -1 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 1 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | applyGammaDecoding: 0 61 | platformSettings: 62 | - serializedVersion: 3 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 256 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 50 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | forceMaximumCompressionQuality_BC6H_BC7: 0 74 | - serializedVersion: 3 75 | buildTarget: Standalone 76 | maxTextureSize: 256 77 | resizeAlgorithm: 0 78 | textureFormat: -1 79 | textureCompression: 1 80 | compressionQuality: 50 81 | crunchedCompression: 0 82 | allowsAlphaSplitting: 0 83 | overridden: 0 84 | androidETC2FallbackOverride: 0 85 | forceMaximumCompressionQuality_BC6H_BC7: 0 86 | spriteSheet: 87 | serializedVersion: 2 88 | sprites: [] 89 | outline: [] 90 | physicsShape: [] 91 | bones: [] 92 | spriteID: 93 | internalID: 0 94 | vertices: [] 95 | indices: 96 | edges: [] 97 | weights: [] 98 | secondaryTextures: [] 99 | spritePackingTag: 100 | pSDRemoveMatte: 0 101 | pSDShowRemoveMatteOption: 0 102 | userData: 103 | assetBundleName: 104 | assetBundleVariant: 105 | -------------------------------------------------------------------------------- /c/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(syntacts_c SHARED "") 2 | add_library(syntacts::syntacts_c ALIAS syntacts_c) 3 | target_sources(syntacts_c PRIVATE "syntacts.h" "syntacts.cpp") 4 | target_link_libraries(syntacts_c PRIVATE syntacts) 5 | set_target_properties(syntacts_c PROPERTIES CXX_STANDARD 17 DEBUG_POSTFIX -d) 6 | install(TARGETS syntacts_c RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 7 | 8 | add_custom_command(TARGET syntacts_c POST_BUILD 9 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 10 | $ 11 | "${PROJECT_SOURCE_DIR}/csharp/examples/example_basic/") 12 | 13 | add_custom_command(TARGET syntacts_c POST_BUILD 14 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 15 | $ 16 | "${PROJECT_SOURCE_DIR}/csharp/examples/example_library/") 17 | 18 | add_custom_command(TARGET syntacts_c POST_BUILD 19 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 20 | $ 21 | "${PROJECT_SOURCE_DIR}/csharp/examples/example_music/") 22 | 23 | add_custom_command(TARGET syntacts_c POST_BUILD 24 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 25 | $ 26 | "${PROJECT_SOURCE_DIR}/csharp/examples/example_sequences/") 27 | 28 | add_custom_command(TARGET syntacts_c POST_BUILD 29 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 30 | $ 31 | "${PROJECT_SOURCE_DIR}/csharp/examples/example_signals/") 32 | 33 | add_custom_command(TARGET syntacts_c POST_BUILD 34 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 35 | $ 36 | "${PROJECT_SOURCE_DIR}/csharp/examples/example_spatializer/") 37 | 38 | add_custom_command(TARGET syntacts_c POST_BUILD 39 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 40 | $ 41 | "${PROJECT_SOURCE_DIR}/csharp/examples/example_devices/") 42 | 43 | add_custom_command(TARGET syntacts_c POST_BUILD 44 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 45 | $ 46 | "${PROJECT_SOURCE_DIR}/python/") 47 | 48 | add_custom_command(TARGET syntacts_c POST_BUILD 49 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 50 | $ 51 | "${PROJECT_SOURCE_DIR}/unity/SyntactsDemo/Assets/Syntacts/Plugins/") 52 | 53 | add_custom_command(TARGET syntacts_c POST_BUILD 54 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 55 | "${PROJECT_SOURCE_DIR}/csharp/Syntacts/Syntacts.cs" 56 | "${PROJECT_SOURCE_DIR}/unity/SyntactsDemo/Assets/Syntacts/") 57 | 58 | -------------------------------------------------------------------------------- /unity/SyntactsDemo/Assets/Syntacts/SyntactsHub.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using Syntacts; 5 | 6 | [DisallowMultipleComponent] 7 | public class SyntactsHub : MonoBehaviour 8 | { 9 | /// The Syntacts session maintained by this SyntactsHub 10 | public Session session; 11 | public Device device { 12 | get { return currentDevice; } 13 | } 14 | 15 | public enum OpenMode { 16 | Default = 0, 17 | ByIndex = 1, 18 | ByName = 2, 19 | ByAPI = 3, 20 | Custom = 4 21 | }; 22 | 23 | [SerializeField] 24 | private OpenMode openMode = OpenMode.Default; 25 | [SerializeField] 26 | int index = 0; 27 | [SerializeField] 28 | int channelCount = 2; 29 | [SerializeField] 30 | int sampleRate = 44100; 31 | 32 | #if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN 33 | [SerializeField] 34 | API deviceApi = API.MME; 35 | #elif UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX 36 | [SerializeField] 37 | API deviceApi = API.CoreAudio; 38 | #else 39 | [SerializeField] 40 | API deviceApi = API.Unknown; 41 | #endif 42 | 43 | [SerializeField] 44 | string deviceName = ""; 45 | 46 | private Device currentDevice; 47 | 48 | void Awake() { 49 | currentDevice = null; 50 | session = new Session(); 51 | int result = -1; 52 | if (openMode == OpenMode.Default) 53 | result = session.Open(); 54 | else if (openMode == OpenMode.ByAPI) 55 | result = session.Open(deviceApi); 56 | else if (openMode == OpenMode.ByIndex) 57 | result = session.Open(index); 58 | else if (openMode == OpenMode.Custom) 59 | result = session.Open(index, channelCount, sampleRate); 60 | else if (openMode == OpenMode.ByName) { 61 | foreach (Device dev in session.availableDevices) { 62 | if (dev.name == deviceName && (deviceApi == API.Unknown || dev.api == deviceApi)) { 63 | result = session.Open(dev.index); 64 | break; 65 | } 66 | } 67 | } 68 | if (result != 0) 69 | Debug.LogError("[Syntacts] Failed to open Device (Error code: " + result.ToString() + ")"); 70 | else { 71 | currentDevice = session.currentDevice; 72 | Debug.Log("[Syntacts] Opened Device " + currentDevice.index.ToString() + ": " + currentDevice.name); 73 | } 74 | } 75 | 76 | void OnApplicationQuit() { 77 | if (session != null && session.isOpen) { 78 | int result = session.Close(); 79 | if (result != 0) 80 | Debug.LogError("[Syntacts] Failed to close Device (Error code: " + result.ToString() + ")"); 81 | else 82 | Debug.Log("[Syntacts] Closed Device"); 83 | session.Dispose(); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /include/Tact/Library.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | #include 28 | #include 29 | 30 | namespace tact { 31 | 32 | /// Formats used for exporting/importing Signals. 33 | enum class FileFormat { 34 | Unknown = -1, ///< unknown format (for internal use only) 35 | Auto = 0, ///< automatic detection from file path extension 36 | SIG = 1, ///< Syntacts file form 37 | WAV = 2, ///< WAV audio file format 38 | AIFF = 3, ///< AIFF audio file format 39 | CSV = 4, ///< comman-separated-value format, 40 | JSON = 5 ///< human readable serialized format 41 | }; 42 | 43 | namespace Library { 44 | 45 | /// Returns the directory to which all library Signals are saved/loaded (usually C:\Users\[user]\AppData\Roaming\Syntacts\Library). 46 | const std::string& getLibraryDirectory(); 47 | 48 | /// Saves a serialized Signal to the global Syntacts Signal library. 49 | bool saveSignal(const Signal& signal, const std::string& name); 50 | 51 | /// Loads a serialized Signal from the global Syntacts Signal library. 52 | bool loadSignal(Signal& signal, const std::string& name); 53 | 54 | /// Erases a Signal from the global Syntacts Signal library if it exists. 55 | bool deleteSignal(const std::string& name); 56 | 57 | /// Saves a Signal as a specified file format. 58 | bool exportSignal(const Signal& signal, const std::string& filePath, FileFormat format = FileFormat::Auto, int sampleRate = 48000, double maxLength = 60); 59 | 60 | /// Imports a Signal of a specific file format. 61 | bool importSignal(Signal& signal, const std::string& filePath, FileFormat format = FileFormat::Auto, int sampleRate = 48000); 62 | 63 | } // namespace Library 64 | 65 | } // namespace tact -------------------------------------------------------------------------------- /include/Tact/Process.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | #include 28 | 29 | namespace tact 30 | { 31 | 32 | /////////////////////////////////////////////////////////////////////////////// 33 | 34 | /// A Signal which repeats another Signal for a number of repetitions. 35 | class SYNTACTS_API Repeater 36 | { 37 | public: 38 | Repeater(); 39 | Repeater(Signal signal, int repetitions, double delay = 0); 40 | double sample(double t) const; 41 | double length() const; 42 | 43 | public: 44 | Signal signal; 45 | int repetitions; 46 | double delay; 47 | 48 | private: 49 | TACT_SERIALIZE(TACT_MEMBER(signal), TACT_MEMBER(repetitions), TACT_MEMBER(delay)); 50 | }; 51 | 52 | /////////////////////////////////////////////////////////////////////////////// 53 | 54 | /// A Signal which stretches or compresses another Signal temporally by a factor. 55 | class SYNTACTS_API Stretcher 56 | { 57 | public: 58 | Stretcher(); 59 | Stretcher(Signal signal, double factor); 60 | double sample(double t) const; 61 | double length() const; 62 | 63 | public: 64 | Signal signal; 65 | double factor; 66 | 67 | private: 68 | TACT_SERIALIZE(TACT_MEMBER(signal), TACT_MEMBER(factor)); 69 | }; 70 | 71 | /////////////////////////////////////////////////////////////////////////////// 72 | 73 | /// A Signal which reverses the input Signal. 74 | class SYNTACTS_API Reverser { 75 | public: 76 | Reverser(); 77 | Reverser(Signal signal); 78 | double sample(double t) const; 79 | double length() const; 80 | public: 81 | Signal signal; 82 | private: 83 | TACT_SERIALIZE(TACT_MEMBER(signal)); 84 | }; 85 | 86 | /////////////////////////////////////////////////////////////////////////////// 87 | 88 | } // namespace tact -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Syntacts is a haptic rendering framework for vibrotactile feedback. It eliminates the need for expensive haptic controllers or custom electronics by leveraging commercial-off-the-shelf audio interfaces. As a complete package, Syntacts provides the software and hardware needed to interface audio devices with low latency, synthesize complex waveforms, and amplify signals to appropriate levels. To learn more, please visit the official website: 4 | 5 | ## [www.syntacts.org](https://www.syntacts.org/) 6 | 7 | # Quick Start 8 | 9 | You can find in-depth [tutorials](https://www.syntacts.org/tutorials/) on the main website, but if you're in a hurry, follow these quick start guides: 10 | 11 | ## Syntacts GUI 12 | - Get the latest [Release](https://github.com/mahilab/Syntacts/releases) and extract the files. 13 | - Run the GUI executable `syntacts_gui` in the top level directory. 14 | - On Windows, you may receive a "Windows protected your PC" screen. Click "More info", then "Run anyway". 15 | - On macOS, the executable `syntacts_gui` may not run. Open a Terminal and run the following command in the directory of `syntacts_gui`. After this, you should be able to successfully run `syntacts_gui`. 16 | 17 | ```shell 18 | > sudo chmod 777 syntacts_gui 19 | ``` 20 | 21 | ## C/C++ API 22 | - Get the source code by pulling the `master` branch on the GitHub repository. 23 | - Build and install **Syntacts** for your system by following the [tutorial](https://www.syntacts.org/tutorials/). 24 | - Use the [template](https://github.com/mahilab/Syntacts/tree/master/template) to make a new Syntacts project with CMake. 25 | 26 | ## C# API 27 | - Get the latest [Release](https://github.com/mahilab/Syntacts/releases) and extract the files. 28 | - Navigate to the `csharp` directory. 29 | - From the command line, build the Syntacts library and then run each example you want to try out: 30 | ```shell 31 | > cd csharp/Syntacts 32 | > dotnet build 33 | > cd ../examples/example_basic 34 | > dotnet run 35 | ``` 36 | 37 | ## Unity 38 | - Get the latest [Release](https://github.com/mahilab/Syntacts/releases) and extract the files. 39 | - Import `unity/syntacts.unitypackage` to your project. 40 | - Add the `SyntactsHub` component to a scene object. 41 | - Use the `session` member variable of `SyntactsHub` to play Syntacts Signals. 42 | 43 | OR 44 | 45 | - Navigate to the `unity/SyntactsDemo/Assets/Demo` directory. 46 | - Open `Demo.unity`. 47 | 48 | ## Python API 49 | - Get the latest [Release](https://github.com/mahilab/Syntacts/releases) and extract the files. 50 | - Navigate to the `python` directory. 51 | - Run any of the `example.py` files: 52 | ```shell 53 | > python example_basic.py 54 | ``` 55 | 56 | # Having issues? 57 | - Check the [Tutorials](https://www.syntacts.org/tutorials/) and [FAQ](https://www.syntacts.org/faq/) pages. 58 | - Head over to the [Issues](https://github.com/mahilab/Syntacts/issues) page and tell us about your problem. 59 | -------------------------------------------------------------------------------- /gui/src/Gui.cpp: -------------------------------------------------------------------------------- 1 | #include "Gui.hpp" 2 | #include "DragAndDrop.hpp" 3 | #include 4 | 5 | using namespace mahi::gui; 6 | 7 | namespace fs = std::filesystem; 8 | 9 | Gui::Gui(const Application::Config config) : 10 | Application(config), 11 | theme(*this), 12 | status(*this), 13 | device(*this), 14 | player(*this), 15 | workspace(*this), 16 | library(*this), 17 | visualizer(*this), 18 | debug(*this) 19 | { 20 | fs::create_directories(saveDir()); 21 | static auto inifile = saveDir() + "imgui.ini"; 22 | ImGui::GetIO().IniFilename = inifile.c_str(); 23 | positionWindows(); 24 | ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_DockingEnable; 25 | } 26 | 27 | void Gui::update() { 28 | positionWindows(); 29 | static ImGuiIO& io = ImGui::GetIO(); 30 | theme.update(); 31 | device.update(); 32 | player.update(); 33 | workspace.update(); 34 | library.update(); 35 | visualizer.update(); 36 | status.update(); 37 | debug.update(); 38 | UpdateDragAndDrop(); 39 | } 40 | 41 | const std::string &Gui::saveDir() 42 | { 43 | #ifdef _WIN32 44 | static fs::path p = std::string(std::getenv("APPDATA")) + std::string("\\Syntacts\\GUI\\"); 45 | static std::string dir = p.generic_string(); 46 | #elif __APPLE__ 47 | static fs::path p = std::string(getenv("HOME")) + "/Library/Syntacts/GUI/"; 48 | static std::string dir = p.generic_string(); 49 | #elif __linux__ 50 | static fs::path p = std::string(std::getenv("HOME")) + "/Syntacts/GUI/"; 51 | static std::string dir = p.generic_string(); 52 | #else 53 | static std::string dir = ""; 54 | #endif 55 | return dir; 56 | } 57 | 58 | void Gui::positionWindows() 59 | { 60 | ImVec2 windowSize = {960, 540}; 61 | ImVec2 vpPos = ImGui::GetMainViewport()->Pos; 62 | float margin = 5; 63 | float barHeight = 33; 64 | float sidePanelWidth = 180; 65 | float workspaceHeight = 300; 66 | float columnHeight = windowSize.y - barHeight - 3 * margin; 67 | float centerWidth = windowSize.x - 2 * (2 * margin + sidePanelWidth); 68 | float fullWidth = windowSize.x - 2 * margin; 69 | float l = margin; 70 | float t = margin; 71 | library.position = vpPos + ImVec2(l,t); 72 | library.size = {sidePanelWidth, columnHeight}; 73 | t += columnHeight + margin; 74 | status.position = vpPos + ImVec2(l,t); 75 | status.size = {fullWidth, barHeight}; 76 | t = margin; 77 | l += sidePanelWidth + margin; 78 | device.position = vpPos + ImVec2(l,t); 79 | device.size = {centerWidth, barHeight}; 80 | t += barHeight + margin; 81 | workspace.position = vpPos + ImVec2(l,t); 82 | workspace.size = {centerWidth, workspaceHeight}; 83 | t += workspaceHeight + margin; 84 | visualizer.position = vpPos + ImVec2(l,t); 85 | visualizer.size = {centerWidth, windowSize.y - workspaceHeight - barHeight * 2 - margin * 5}; 86 | t = margin; 87 | l += centerWidth + margin; 88 | player.position = vpPos + ImVec2(l,t); 89 | player.size = {sidePanelWidth, columnHeight}; 90 | } 91 | -------------------------------------------------------------------------------- /include/Tact/Config.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | // Syntacts version numbers 28 | #define SYNTACTS_VERSION_MAJOR 1 29 | #define SYNTACTS_VERSION_MINOR 3 30 | #define SYNTACTS_VERSION_PATCH 0 31 | 32 | /// The maximum number of signals that can be played in unison (polyphony) on a single channel 33 | #define SYNTACTS_MAX_VOICES 8 34 | 35 | /// If uncommented, Signals will use a fixed size memory pool for allocation. 36 | /// At this time, there doesn't seem to a great deal of benifit from doing this, 37 | /// but one day it may be be possible to reap the benifits of 38 | /// cache coherence by using a more efficient pool scheme. 39 | // #define SYNTACTS_USE_POOL 40 | 41 | /// The size of pool memory blocks in bytes available to store Signals 42 | /// (only relevant if SYNTACTS_USE_POOL enabled) 43 | // #define SYNTACTS_POOL_BLOCK_SIZE 64 44 | 45 | /// The number of pool blocks, effectively maximum number of signals that can 46 | /// simultaneously exist (only relevant if SYNTACTS_USE_POOL enabled) 47 | // #define SYNTACTS_POOL_MAX_BLOCKS 1024 48 | 49 | /// If uncommented, Signals will use shared pointers internally instead of unique pointers. 50 | /// This will result in fewer copies and allocations, but can lead to thread safety issues 51 | /// if you modify the parametes of Signals after they have been passed to a Session. This 52 | /// effectively breaks the GUI as it is currently implemented, so be warned! 53 | // #define SYNTACTS_USE_SHARED_PTR 54 | 55 | #ifndef SYNTACTS_STATIC 56 | #ifdef SYNTACTS_EXPORTS 57 | #define SYNTACTS_API __declspec(dllexport) 58 | #else 59 | #define SYNTACTS_API __declspec(dllimport) 60 | #endif 61 | 62 | #ifdef _MSC_VER 63 | #pragma warning(disable:4251) 64 | #endif 65 | #else 66 | #define SYNTACTS_API 67 | #endif -------------------------------------------------------------------------------- /src/Filesystem.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Syntacts 4 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab - Rice University 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // Author(s): Evan Pezent, Brandon Cambio 17 | 18 | // Checking filesystem to include 19 | #ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 20 | 21 | // Check for feature test macro for 22 | # if defined(__cpp_lib_filesystem) 23 | # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 24 | 25 | // Check for feature test macro for 26 | # elif defined(__cpp_lib_experimental_filesystem) 27 | # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 28 | 29 | // Assume experimental to be safe 30 | # elif !defined(__has_include) 31 | # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 32 | 33 | // Check if the header "" exists 34 | # elif __has_include() 35 | 36 | // If Visual Studio and not compiling C++17, use experimental 37 | # ifdef _MSC_VER 38 | 39 | // Check and include header that defines "_HAS_CXX17" 40 | # if __has_include() 41 | # include 42 | 43 | // Check for enabled C++17 support 44 | # if defined(_HAS_CXX17) && _HAS_CXX17 45 | # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 46 | # endif 47 | # endif 48 | 49 | // If the marco isn't defined yet, use experimental 50 | # ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 51 | # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 52 | # endif 53 | 54 | // If not on Visual Studio. Use normal 55 | # else // #ifdef _MSC_VER 56 | # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 0 57 | # endif 58 | 59 | // Check if the header "" exists 60 | # elif __has_include() 61 | # define INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 1 62 | 63 | // Fail if neither header is available with a nice error message 64 | # else 65 | # error Could not find system header "" or "" 66 | # endif 67 | 68 | // If experimental version is needed 69 | # if INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 70 | // Include it 71 | # include 72 | 73 | // Alias from std::experimental::filesystem to std::filesystem 74 | namespace std { 75 | namespace filesystem = experimental::filesystem; 76 | } 77 | 78 | // If compiler can use the normal version 79 | # else 80 | # include 81 | # endif 82 | 83 | #endif // #ifndef INCLUDE_STD_FILESYSTEM_EXPERIMENTAL 84 | -------------------------------------------------------------------------------- /csharp/examples/example_basic/example_basic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Syntacts; 4 | 5 | // run with the following console command: 6 | // dotnet run 7 | 8 | class Example 9 | { 10 | static void Main(string[] args) 11 | { 12 | // Syntacts usage begins with creating an audio context, or Session 13 | Session session = new Session(); 14 | 15 | // Now let's open a device ... 16 | 17 | // Usually, you would use "session.open(i)"" to open a specific device with index i 18 | 19 | // Alternatively, you can just open the system default device by passing no arguments 20 | // (which this example does because we don't know what device numbers you might have!) 21 | session.Open(); 22 | 23 | 24 | //------------------------------------------------------------------------- 25 | 26 | // Now, let's create some vibrations ... 27 | 28 | // Vibrations are represented by Signals and combinations of Signals 29 | 30 | // Some Signals (e.g. oscillators) have an INFINITE duration 31 | Signal sig1 = new Sine(440); // a 440 Hz sinewave 32 | Console.WriteLine(sig1.length); // inf 33 | 34 | // Other Signals (e.g. envelopes) have FINITE duration 35 | Signal sig2 = new ASR(1,3,1); // a 5 second attack, sustain, release envelope 36 | Console.WriteLine(sig2.length); // 5 37 | 38 | // Signals can be combined using math operation 39 | Signal sig3 = sig1 * sig2; // a 5 second 440 Hz sinewave with an ASR envelope 40 | Console.WriteLine(sig3.length); // 5 41 | 42 | // Such operations can be done in a single line 43 | Signal sig4 = new Square(880) * new Sine(10) * new ADSR(1,1,1,1); // 880 Hz square, amplitude modulated with 10 Hz sine and 4 s ADSR envelope 44 | Console.WriteLine(sig4.length); // 4 45 | 46 | // For more advanced Signal synthesis, see "example_signals.cpp" 47 | 48 | //------------------------------------------------------------------------- 49 | 50 | // Now that we have some Signals, let's play them ... 51 | 52 | // Play sig1 on channel 0 of the open Device 53 | session.Play(0, sig1); 54 | // The Signal will immediately start playing in the Session's audio thread, 55 | // but we need to sleep this thread so that the program doesn't continue prematurely 56 | Sleep(3); 57 | // Now, we stop the Signal on channel 0 (sig1 will have played for 3 seconds) 58 | session.Stop(0); 59 | 60 | // Let's play another on channel 1... 61 | session.Play(1, sig3); 62 | Sleep(sig3.length); 63 | // We don't have to call session.stop(1) because sig3 is FINITE 64 | 65 | // You can also play a Signal on all channels 66 | session.PlayAll(sig4); 67 | Sleep(sig4.length); 68 | 69 | //------------------------------------------------------------------------- 70 | 71 | // It is important to dispose of the session at the end of your program! 72 | session.Dispose(); 73 | 74 | //------------------------------------------------------------------------- 75 | 76 | // This was an extremely basic example of using Syntacts. See the other 77 | // examples for more complex usage and other included features! 78 | } 79 | static void Sleep(double seconds) { 80 | Thread.Sleep((int)(seconds*1000)); 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /csharp/examples/example_library/example_library.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Syntacts; 4 | 5 | // run with the following console command: 6 | // dotnet run 7 | 8 | class Example 9 | { 10 | // Show pass/fail for testing purposes (you don't need this) 11 | static void showResult(bool r) { 12 | if (r){ 13 | Console.WriteLine("Pass"); 14 | } 15 | else{ 16 | Console.WriteLine("Fail"); 17 | } 18 | } 19 | static void Main(string[] args) 20 | { 21 | // Make a Signal to save/export, and a blank Signal to import in to 22 | Signal save = new Sine(440) * new ASR(1,1,1); 23 | Signal loaded; 24 | 25 | // Syntacts Binary Format (Default Location, i.e. APPDATA/Syntacts/Library) 26 | 27 | showResult( Library.SaveSignal(save, "cs")); 28 | showResult( Library.LoadSignal(out loaded, "cs")); 29 | 30 | // Syntacts Binary Format (Custom Location) 31 | 32 | showResult( Library.ExportSignal(save, "cs.sig")); 33 | showResult( Library.ImportSignal(out loaded, "cs.sig")); 34 | 35 | showResult( Library.ExportSignal(save, "relative/folder/cs.sig")); 36 | showResult( Library.ImportSignal(out loaded, "relative/folder/cs.sig")); 37 | 38 | showResult( Library.ExportSignal(save, "/absolute/folder/cs.sig")); 39 | showResult( Library.ImportSignal(out loaded, "/absolute/folder/cs.sig")); 40 | 41 | // Syntacts JSON Format 42 | 43 | showResult( Library.ExportSignal(save, "cs.json")); 44 | showResult( Library.ImportSignal(out loaded, "cs.json")); 45 | 46 | showResult( Library.ExportSignal(save, "relative/folder/cs.json")); 47 | showResult( Library.ImportSignal(out loaded, "relative/folder/cs.json")); 48 | 49 | showResult( Library.ExportSignal(save, "/absolute/folder/cs.json")); 50 | showResult( Library.ImportSignal(out loaded, "/absolute/folder/cs.json")); 51 | 52 | // // WAV Format 53 | 54 | showResult( Library.ExportSignal(save, "cs.wav")); 55 | showResult( Library.ImportSignal(out loaded, "cs.wav")); 56 | 57 | showResult( Library.ExportSignal(save, "relative/folder/cs.wav")); 58 | showResult( Library.ImportSignal(out loaded, "relative/folder/cs.wav")); 59 | 60 | showResult( Library.ExportSignal(save, "/absolute/folder/cs.wav")); 61 | showResult( Library.ImportSignal(out loaded, "/absolute/folder/cs.wav")); 62 | 63 | // // AIFF Format 64 | 65 | showResult( Library.ExportSignal(save, "cs.aiff")); 66 | showResult( Library.ImportSignal(out loaded, "cs.aiff")); 67 | 68 | showResult( Library.ExportSignal(save, "relative/folder/cs.aiff")); 69 | showResult( Library.ImportSignal(out loaded, "relative/folder/cs.aiff")); 70 | 71 | showResult( Library.ExportSignal(save, "/absolute/folder/cs.aiff")); 72 | showResult( Library.ImportSignal(out loaded, "/absolute/folder/cs.aiff")); 73 | 74 | // // CSV/TXT Format (import not yet supported) 75 | 76 | showResult( Library.ExportSignal(save, "cs.csv")); 77 | showResult( Library.ExportSignal(save, "relative/folder/cs.txt")); 78 | showResult( Library.ExportSignal(save, "/absolute/folder/cs.txt")); 79 | 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /include/Tact/Util.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace tact { 33 | 34 | /////////////////////////////////////////////////////////////////////////////// 35 | 36 | constexpr double PI = 3.14159265358979323846; 37 | constexpr double HALF_PI = 0.5 * PI; 38 | constexpr double TWO_PI = 2.0 * PI; 39 | constexpr double INV_PI = 1.0 / PI; 40 | constexpr double INF = std::numeric_limits::infinity(); 41 | constexpr double EPS = std::numeric_limits::epsilon(); 42 | 43 | /////////////////////////////////////////////////////////////////////////////// 44 | 45 | /// Clampes value between min and max 46 | inline double clamp(double value, double min, double max) { 47 | return value <= min ? min : value >= max ? max : value; 48 | } 49 | 50 | /// Clampes value between 0 and 1 51 | inline double clamp01(double value) { 52 | return value <= 0.0 ? 0.0 : value >= 1.0 ? 1.0 : value; 53 | } 54 | 55 | /// Remaps x from range [x0,x1] to range [y0,y1] 56 | inline double remap(double x, double x0, double x1, double y0, double y1) { 57 | return y0 + (x - x0) * (y1 - y0) / (x1 - x0); 58 | } 59 | 60 | /// Linearly interpolates from a to b given interpolant t in range [0,1] 61 | inline double lerp(double a, double b, double t) { 62 | return a + (b - a) * t; 63 | } 64 | 65 | /////////////////////////////////////////////////////////////////////////////// 66 | 67 | /// Sleeps the calling thread for seconds (accurate within a few milliseconds) 68 | void sleep(double seconds, double max = 60); 69 | 70 | /// Starts a stopwatch timer. Consecutive calls will restart the timer. 71 | void tic(); 72 | 73 | /// Returns the time in seconds since tic() was called. 74 | double toc(); 75 | 76 | /////////////////////////////////////////////////////////////////////////////// 77 | 78 | // Forward declarations 79 | class Signal; 80 | 81 | /// Returns the string name of a Signal 82 | const std::string& signalName(const Signal& signal); 83 | 84 | /// Recurse a signal for embedded signals and calls func on each 85 | void recurseSignal(const Signal& signal, std::function func); 86 | 87 | /////////////////////////////////////////////////////////////////////////////// 88 | 89 | /// Returns the Syntacts version number (e.g. "1.0.0") 90 | const std::string& syntactsVersion(); 91 | 92 | } // namespace tact 93 | -------------------------------------------------------------------------------- /examples/example_basic.cpp: -------------------------------------------------------------------------------- 1 | #include // include Syntacts in its entirety 2 | #include 3 | 4 | using namespace tact; // everything in Syntacts is under the namespace tact 5 | 6 | int main(int argc, char const *argv[]) 7 | { 8 | //------------------------------------------------------------------------- 9 | 10 | // Syntacts usage begins with creating an audio context, or Session 11 | Session session; 12 | 13 | // With a Session, we can enumerate the Device map to see what audio 14 | // interfaces are available for us to use 15 | for (auto& item : session.getAvailableDevices()) { 16 | int idx = item.first; // the device index is the map key 17 | const Device& dev = item.second; // the device struct is the map value 18 | std::cout << idx << " " << dev.name << std::endl; 19 | } 20 | 21 | // Now let's open a device ... 22 | 23 | // Usually, you would use "session.open(i)"" to open a specific device with index i 24 | 25 | // Alternatively, you can just open the system default device by passing no arguments 26 | // (which this example does because we don't know what device numbers you might have!) 27 | session.open(); 28 | 29 | 30 | //------------------------------------------------------------------------- 31 | 32 | // Now, let's create some vibrations ... 33 | 34 | // Vibrations are represented by Signals and combinations of Signals 35 | 36 | // Some Signals (e.g. oscillators) have an INFINITE duration 37 | Signal sig1 = Sine(440); // a 440 Hz sinewave 38 | std::cout << sig1.length() << std::endl; // inf 39 | 40 | // Other Signals (e.g. envelopes) have FINITE duration 41 | Signal sig2 = ASR(1,3,1); // a 5 second attack, sustain, release envelope 42 | std::cout << sig2.length() << std::endl; // 5 43 | 44 | // Signals can be combined using math operation 45 | Signal sig3 = sig1 * sig2; // a 5 second 440 Hz sinewave with an ASR envelope 46 | std::cout << sig3.length() << std::endl; // 5 47 | 48 | // Such operations can be done in a single line 49 | Signal sig4 = Square(880) * Sine(10) * ADSR(1,1,1,1); // 880 Hz square, amplitude modulated with 10 Hz sine and 4 s ADSR envelope 50 | std::cout << sig4.length() << std::endl; // 4 51 | 52 | // For more advanced Signal synthesis, see "example_signals.cpp" 53 | 54 | //------------------------------------------------------------------------- 55 | 56 | // Now that we have some Signals, let's play them ... 57 | 58 | // Play sig1 on channel 0 of the open Device 59 | session.play(0, sig1); 60 | // The Signal will immediately start playing in the Session's audio thread, 61 | // but we need to sleep this thread so that the program doesn't continue prematurely 62 | sleep(3); 63 | // Now, we stop the Signal on channel 0 (sig1 will have played for 3 seconds) 64 | session.stop(0); 65 | 66 | // Let's play another on channel 1... 67 | session.play(1, sig3); 68 | sleep(sig3.length()); 69 | // We don't have to call session.stop(1) because sig3 is FINITE 70 | 71 | // You can also play a Signal on all channels 72 | session.playAll(sig4); 73 | sleep(sig4.length()); 74 | 75 | //------------------------------------------------------------------------- 76 | 77 | // Devices will automatically close when the Session goes out of scope, 78 | // but it is good practice to do this explicitly 79 | 80 | session.close(); 81 | 82 | //------------------------------------------------------------------------- 83 | 84 | // This was an extremely basic example of using Syntacts. See the other 85 | // examples for more complex usage and other included features! 86 | 87 | } 88 | -------------------------------------------------------------------------------- /include/Tact/Operator.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | #include 28 | 29 | namespace tact { 30 | 31 | /////////////////////////////////////////////////////////////////////////////// 32 | 33 | /// A signal which is the result of operating on two other Signals. 34 | struct IOperator { 35 | IOperator() = default; 36 | IOperator(Signal lhs, Signal rhs); 37 | public: 38 | Signal lhs, rhs; 39 | private: 40 | TACT_SERIALIZE(TACT_MEMBER(lhs), TACT_MEMBER(rhs)); 41 | }; 42 | 43 | /////////////////////////////////////////////////////////////////////////////// 44 | 45 | /// A Signal which is the sum or difference of two other signals. 46 | struct Sum : public IOperator { 47 | using IOperator::IOperator; 48 | double sample(double t) const; 49 | double length() const; 50 | private: 51 | TACT_SERIALIZE(TACT_PARENT(IOperator)); 52 | }; 53 | 54 | /////////////////////////////////////////////////////////////////////////////// 55 | 56 | /// A Signal which is the product of two other signals. 57 | struct Product : public IOperator { 58 | using IOperator::IOperator; 59 | double sample(double t) const; 60 | double length() const; 61 | private: 62 | TACT_SERIALIZE(TACT_PARENT(IOperator)); 63 | }; 64 | 65 | /////////////////////////////////////////////////////////////////////////////// 66 | 67 | /// Multiply two Signals. 68 | inline Signal operator*(Signal lhs, Signal rhs); 69 | /// Multiply a scalar and a Signal. 70 | inline Signal operator*(double lhs, Signal rhs); 71 | /// Multiply a Signal and a scalar. 72 | inline Signal operator*(Signal lhs, double rhs); 73 | /// Multiply a Signal and a scalar. 74 | inline Signal& operator*=(Signal& lhs, double rhs); 75 | 76 | /// Add two Signals. 77 | inline Signal operator+(Signal lhs, Signal rhs); 78 | /// Add a scalar and a Signal. 79 | inline Signal operator+(double lhs, Signal rhs); 80 | /// Add a Signal and a scalar. 81 | inline Signal operator+(Signal lhs, double rhs); 82 | /// Add a Signal and a scalar. 83 | inline Signal& operator+=(Signal& lhs, double rhs); 84 | 85 | /// Subtract two Signals. 86 | inline Signal operator-(Signal lhs, Signal rhs); 87 | /// Subtract a Signal from a scalar. 88 | inline Signal operator-(double lhs, Signal rhs); 89 | /// Subtract a scalar from a Signal. 90 | inline Signal operator-(Signal lhs, double rhs); 91 | /// Subtract a scalar from a Signal. 92 | inline Signal& operator-=(Signal& lhs, double rhs); 93 | 94 | /// Negate a Signal. 95 | inline Signal operator-(Signal lhs); 96 | 97 | /////////////////////////////////////////////////////////////////////////////// 98 | 99 | } // namespace tact 100 | 101 | #include 102 | -------------------------------------------------------------------------------- /include/Tact/Sequence.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | #include 28 | #include 29 | 30 | namespace tact { 31 | 32 | /////////////////////////////////////////////////////////////////////////////// 33 | 34 | /// A Signal formed by the sequencing of other Signals at various time points. 35 | class Sequence { 36 | public: 37 | 38 | /// A Key in the Sequence. 39 | struct Key { 40 | double t; 41 | Signal signal; 42 | TACT_SERIALIZE(TACT_MEMBER(t), TACT_MEMBER(signal)); 43 | }; 44 | 45 | /// Default constructor. 46 | Sequence(); 47 | /// Moves the insertion head forward/backward by t. 48 | Sequence& push(double t); 49 | /// Pushes a Signal at the head position and then moves the head forward. 50 | Sequence& push(Signal signal); 51 | /// Pushes another Sequence at the head position and then moves the head forward. 52 | Sequence& push(Sequence sequence); 53 | /// Inserts a Signal at position t in this Sequence but does NOT move head. 54 | Sequence& insert(Signal signal, double t); 55 | /// Inserts another Sequence at position t in this Sequence but does NOT move head. 56 | Sequence& insert(Sequence sequence, double t); 57 | /// Clears the Sequence 58 | void clear(); 59 | 60 | /// Samples and sums all overlapping signals in the sequence at time t. 61 | double sample(double t) const; 62 | /// Returns the length of the Sequence. 63 | double length() const; 64 | 65 | /// Returns the number of keys in the sequence. 66 | int keyCount() const; 67 | /// Returns a key in the sequence. 68 | const Key& getKey(int idx) const; 69 | 70 | /// Moves the insertion head of this Sequence by the specified amount. 71 | inline Sequence& operator<<(double rhs); 72 | /// Pushes a Signal into this Sequence at the current insertion head. 73 | inline Sequence& operator<<(Signal rhs); 74 | /// Pushes another Sequence into this Sequence at the current insertion head. 75 | inline Sequence& operator<<(Sequence rhs); 76 | 77 | public: 78 | double head; ///< the current insertion head position/time. 79 | private: 80 | std::vector m_keys; ///< all keys 81 | double m_length; ///< accumulated length 82 | private: 83 | TACT_SERIALIZE(TACT_MEMBER(head), TACT_MEMBER(m_keys), TACT_MEMBER(m_length)); 84 | }; 85 | 86 | /////////////////////////////////////////////////////////////////////////////// 87 | 88 | /// Creates a new Sequence from two Signals. 89 | inline Sequence operator<<(Signal lhs, Signal rhs); 90 | /// Creates a new Sequence from a Signal and moves the head forward. 91 | inline Sequence operator<<(Signal lhs, double rhs); 92 | /// Creates a new Sequence, moves the head forward, then inserts a Signal. 93 | inline Sequence operator<<(double lhs, Signal rhs); 94 | 95 | /////////////////////////////////////////////////////////////////////////////// 96 | 97 | } // namespace tact 98 | 99 | #include -------------------------------------------------------------------------------- /include/Tact/Detail/Signal.inl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace tact 4 | { 5 | 6 | template 7 | Signal::Signal(T signal) : 8 | gain(1), 9 | bias(0), 10 | #ifndef SYNTACTS_USE_POOL 11 | #ifdef SYNTACTS_USE_SHARED_PTR 12 | m_ptr(std::make_shared>(std::move(signal))) 13 | #else 14 | m_ptr(std::make_unique>(std::move(signal))) 15 | #endif 16 | #else 17 | #ifdef SYNTACTS_USE_SHARED_PTR 18 | m_ptr(std::allocate_shared, Allocator>(Allocator(), std::move(signal))) 19 | #else 20 | m_ptr(new (Signal::pool().allocate()) Model(std::move(signal))) 21 | #endif 22 | #endif 23 | { 24 | #ifdef SYNTACTS_USE_POOL 25 | #ifdef SYNTACTS_USE_SHARED_PTR 26 | static_assert((2 * sizeof(void *) + sizeof(Model)) <= SYNTACTS_POOL_BLOCK_SIZE, "Signal allocation would exceed SIGNAL_BLOCK SIZE"); 27 | #else 28 | static_assert((sizeof(Model)) <= SYNTACTS_POOL_BLOCK_SIZE, "Signal allocation would exceed SIGNAL_BLOCK SIZE"); 29 | #endif 30 | #endif 31 | } 32 | 33 | inline double Signal::sample(double t) const 34 | { 35 | return m_ptr->sample(t) * gain + bias; 36 | } 37 | 38 | inline void Signal::sample(const double *t, double *b, int n) const 39 | { 40 | return m_ptr->sample(t, b, n, gain, bias); 41 | } 42 | 43 | inline double Signal::length() const 44 | { 45 | return m_ptr->length(); 46 | } 47 | 48 | template 49 | inline bool Signal::isType() const 50 | { 51 | return m_ptr->typeId() == typeid(T); 52 | } 53 | 54 | template inline T* Signal::getAs() const { 55 | return static_cast(m_ptr->get()); 56 | } 57 | 58 | #ifdef SYNTACTS_USE_POOL 59 | inline Signal::Pool& Signal::pool() { 60 | static Signal::Pool p; 61 | return p; 62 | } 63 | #endif 64 | 65 | inline int Signal::count() { 66 | return Concept::count(); 67 | } 68 | 69 | /////////////////////////////////////////////////////////////////////////////// 70 | 71 | template 72 | Signal::Model::Model() 73 | {} 74 | 75 | template 76 | Signal::Model::Model(T model) : 77 | m_model(std::move(model)) 78 | { } 79 | 80 | template 81 | double Signal::Model::sample(double t) const 82 | { 83 | return m_model.sample(t); 84 | } 85 | 86 | template 87 | void Signal::Model::sample(const double* t, double* b, int n, double s, double o) const 88 | { 89 | for (int i = 0; i < n; ++i) 90 | b[i] = m_model.sample(t[i]) * s + o; 91 | } 92 | 93 | template 94 | double Signal::Model::length() const 95 | { 96 | return m_model.length(); 97 | } 98 | 99 | template 100 | std::type_index Signal::Model::typeId() const 101 | { 102 | static std::type_index id = typeid(T); 103 | return id; 104 | } 105 | 106 | template 107 | void* Signal::Model::get() const 108 | { 109 | return (void*)&m_model; 110 | } 111 | 112 | #ifndef SYNTACTS_USE_SHARED_PTR 113 | #ifndef SYNTACTS_USE_POOL 114 | 115 | template 116 | std::unique_ptr Signal::Model::copy() const 117 | { 118 | s_count++; 119 | return std::make_unique>(*this); 120 | } 121 | 122 | #else 123 | 124 | template 125 | std::unique_ptr Signal::Model::copy() const 126 | { 127 | s_count++; 128 | return std::unique_ptr(new (Signal::pool().allocate()) Model(*this)); 129 | } 130 | 131 | #endif 132 | #endif 133 | 134 | /////////////////////////////////////////////////////////////////////////////// 135 | 136 | template 137 | void Signal::save(Archive& archive) const { 138 | archive(TACT_MEMBER(gain), TACT_MEMBER(bias), TACT_MEMBER(m_ptr)); 139 | } 140 | 141 | template 142 | void Signal::load(Archive& archive) { 143 | #if defined SYNTACTS_USE_POOL && !defined SYNTACTS_USE_SHARED_PTR 144 | std::unique_ptr ptr; 145 | archive(TACT_MEMBER(gain), TACT_MEMBER(bias), TACT_MEMBER(ptr)); 146 | m_ptr = ptr->copy(); 147 | #else 148 | archive(TACT_MEMBER(gain), TACT_MEMBER(bias), TACT_MEMBER(m_ptr)); 149 | #endif 150 | } 151 | 152 | /////////////////////////////////////////////////////////////////////////////// 153 | 154 | } // namespace tact 155 | -------------------------------------------------------------------------------- /include/Tact/Oscillator.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | #include 28 | 29 | namespace tact 30 | { 31 | 32 | /////////////////////////////////////////////////////////////////////////////// 33 | 34 | /// Interface class for objects which produce a periodic, oscillating signal. 35 | class SYNTACTS_API IOscillator 36 | { 37 | public: 38 | /// Default constructor. 39 | IOscillator(); 40 | /// Constucts an Oscillator with a signal based input. 41 | IOscillator(Signal x); 42 | /// Constructs an Oscillator with a scalar frequency in hertz. 43 | IOscillator(double hertz); 44 | /// Constructs a "chirp" Oscillator with an initial frequency and ramp rate in hertz. 45 | IOscillator(double initial, double rate); 46 | /// Constructs frequency modulated (FM) Oscillator. 47 | IOscillator(double hertz, Signal modulation, double index = 2.0); 48 | /// Returns infinity 49 | inline double length() const; 50 | public: 51 | Signal x; ///< the Oscillator's input. 52 | private: 53 | TACT_SERIALIZE(TACT_MEMBER(x)); 54 | }; 55 | 56 | /////////////////////////////////////////////////////////////////////////////// 57 | 58 | /// A sine wave Oscillator. 59 | class SYNTACTS_API Sine : public IOscillator 60 | { 61 | public: 62 | using IOscillator::IOscillator; 63 | inline double sample(double t) const; 64 | private: 65 | TACT_SERIALIZE(TACT_PARENT(IOscillator)); 66 | }; 67 | 68 | /////////////////////////////////////////////////////////////////////////////// 69 | 70 | /// A square wave Oscillator. 71 | class SYNTACTS_API Square : public IOscillator 72 | { 73 | public: 74 | using IOscillator::IOscillator; 75 | inline double sample(double t) const; 76 | private: 77 | TACT_SERIALIZE(TACT_PARENT(IOscillator)); 78 | }; 79 | 80 | /////////////////////////////////////////////////////////////////////////////// 81 | 82 | /// A saw wave Oscillator. 83 | class SYNTACTS_API Saw : public IOscillator 84 | { 85 | public: 86 | using IOscillator::IOscillator; 87 | inline double sample(double t) const; 88 | private: 89 | TACT_SERIALIZE(TACT_PARENT(IOscillator)); 90 | }; 91 | 92 | /////////////////////////////////////////////////////////////////////////////// 93 | 94 | /// A triangle wave Oscillator. 95 | class SYNTACTS_API Triangle : public IOscillator 96 | { 97 | public: 98 | using IOscillator::IOscillator; 99 | inline double sample(double t) const; 100 | private: 101 | TACT_SERIALIZE(TACT_PARENT(IOscillator)); 102 | }; 103 | 104 | /////////////////////////////////////////////////////////////////////////////// 105 | 106 | /// A PWM square wave with adjustable frequency and duty cycle. 107 | class SYNTACTS_API Pwm 108 | { 109 | public: 110 | /// Constructor 111 | Pwm(double frequency = 1.0, double dutyCycle = 0.5); 112 | inline double sample(double t) const; 113 | inline double length() const; 114 | public: 115 | double frequency; 116 | double dutyCycle; 117 | private: 118 | TACT_SERIALIZE(TACT_MEMBER(frequency), TACT_MEMBER(dutyCycle)); 119 | }; 120 | 121 | /////////////////////////////////////////////////////////////////////////////// 122 | 123 | 124 | } // namespace tact 125 | 126 | #include -------------------------------------------------------------------------------- /gui/src/DragAndDrop.cpp: -------------------------------------------------------------------------------- 1 | #define IMGUI_DEFINE_MATH_OPERATORS 2 | 3 | #include "DragAndDrop.hpp" 4 | #include 5 | 6 | namespace 7 | { 8 | bool g_signalHeld = false; 9 | bool g_paletteHeld = false; 10 | PItem g_palettePayload; 11 | std::pair g_signalPayload; 12 | bool g_pulsableSet = false; 13 | float g_pulseTime; 14 | 15 | static inline ImVec4 Lerp(const ImVec4& a, const ImVec4& b, float t) 16 | { 17 | return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); 18 | } 19 | 20 | } // namespace 21 | 22 | void UpdateDragAndDrop() 23 | { 24 | g_pulseTime = ImGui::GetIO().DeltaTime; 25 | if (g_signalHeld && (ImGui::GetIO().MouseReleased[0] || !ImGui::GetIO().MouseDown[0])) 26 | g_signalHeld = false; 27 | if (g_paletteHeld && (ImGui::GetIO().MouseReleased[0] || !ImGui::GetIO().MouseDown[0])) 28 | g_paletteHeld = false; 29 | } 30 | 31 | void BeginPulsable(bool acceptPalette, bool acceptSignal, ImGuiCol col) 32 | { 33 | ImVec4 color = ImGui::GetStyle().Colors[col]; 34 | if ((g_signalHeld && acceptSignal) || (g_paletteHeld && acceptPalette)) 35 | { 36 | static float f = 1.0f; 37 | float t = 0.5f + 0.5f * std::sin(2.0f * IM_PI * f * (float)ImGui::GetTime()); 38 | color = Lerp(color, ImVec4(0, 0, 0, 0), t * 0.25f); 39 | ImGui::PushStyleColor(col, color); 40 | g_pulsableSet = true; 41 | } 42 | } 43 | 44 | void EndPulsable() 45 | { 46 | if (g_pulsableSet) { 47 | ImGui::PopStyleColor(); 48 | g_pulsableSet = false; 49 | } 50 | } 51 | 52 | void SetPaletteHeld(bool held) 53 | { 54 | g_paletteHeld = held; 55 | if (held) { 56 | g_pulseTime = 0; 57 | } 58 | } 59 | 60 | void PaletteSource(PItem pItem) 61 | { 62 | if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) 63 | { 64 | g_palettePayload = pItem; 65 | SetPaletteHeld(true); 66 | ImGui::SetDragDropPayload("DND_PITEM", &g_palettePayload, sizeof(PItem)); 67 | ImGui::Text(paletteName(pItem).c_str()); 68 | ImGui::EndDragDropSource(); 69 | } 70 | } 71 | 72 | bool PaletteTarget() 73 | { 74 | bool ret = false; 75 | if (ImGui::BeginDragDropTarget()) 76 | { 77 | if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("DND_PITEM")) 78 | { 79 | ret = true; 80 | } 81 | ImGui::EndDragDropTarget(); 82 | } 83 | return ret; 84 | } 85 | 86 | PItem PalettePayload() 87 | { 88 | return g_palettePayload; 89 | } 90 | 91 | bool PaletteHeld() { 92 | return g_paletteHeld; 93 | } 94 | 95 | void SetSignalHeld(bool held) 96 | { 97 | g_signalHeld = held; 98 | if (held) { 99 | g_pulseTime = 0; 100 | } 101 | } 102 | 103 | void SignalSource(const std::string &name, tact::Signal signal) 104 | { 105 | if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) 106 | { 107 | g_signalPayload.first = name; 108 | g_signalPayload.second = std::move(signal); 109 | SetSignalHeld(true); 110 | ImGui::SetDragDropPayload("DND_LITEM", &g_signalPayload, sizeof(std::string)); 111 | ImGui::Text(name.c_str()); 112 | ImGui::EndDragDropSource(); 113 | } 114 | } 115 | 116 | bool SignalTarget() 117 | { 118 | bool ret = false; 119 | if (ImGui::BeginDragDropTarget()) 120 | { 121 | const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("DND_LITEM"); 122 | if (payload != NULL && g_signalHeld) 123 | { 124 | g_signalHeld = false; 125 | ret = true; 126 | } 127 | ImGui::EndDragDropTarget(); 128 | } 129 | return ret; 130 | } 131 | 132 | const std::pair& SignalPayload() 133 | { 134 | return g_signalPayload; 135 | } 136 | 137 | bool SignalHeld() 138 | { 139 | return g_signalHeld; 140 | } 141 | 142 | void HelpSource() { 143 | if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) 144 | { 145 | static int helpPayload = 1; 146 | ImGui::SetDragDropPayload("DND_HELP", &helpPayload, sizeof(int)); 147 | ImGui::Text(ICON_FA_QUESTION); 148 | ImGui::EndDragDropSource(); 149 | } 150 | } 151 | 152 | bool HelpTarget() 153 | { 154 | bool ret = false; 155 | if (ImGui::BeginDragDropTarget()) 156 | { 157 | if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("DND_HELP")) 158 | { 159 | ret = true; 160 | } 161 | ImGui::EndDragDropTarget(); 162 | } 163 | return ret; 164 | } -------------------------------------------------------------------------------- /csharp/.gitignore: -------------------------------------------------------------------------------- 1 | # The following command works for downloading when using Git for Windows: 2 | # curl -LOf http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 3 | # 4 | # Download this file using PowerShell v3 under Windows with the following comand: 5 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 6 | # 7 | # or wget: 8 | # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore 9 | 10 | # User-specific files 11 | *.suo 12 | *.user 13 | *.sln.docstates 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Rr]elease/ 18 | x64/ 19 | [Bb]in/ 20 | [Oo]bj/ 21 | # build folder is nowadays used for build scripts and should not be ignored 22 | #build/ 23 | 24 | # NuGet Packages 25 | *.nupkg 26 | # The packages folder can be ignored because of Package Restore 27 | **/packages/* 28 | # except build/, which is used as an MSBuild target. 29 | !**/packages/build/ 30 | # Uncomment if necessary however generally it will be regenerated when needed 31 | #!**/packages/repositories.config 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | *_i.c 38 | *_p.c 39 | *.ilk 40 | *.meta 41 | *.obj 42 | *.pch 43 | *.pdb 44 | *.pgc 45 | *.pgd 46 | *.rsp 47 | *.sbr 48 | *.tlb 49 | *.tli 50 | *.tlh 51 | *.tmp 52 | *.tmp_proj 53 | *.log 54 | *.vspscc 55 | *.vssscc 56 | .builds 57 | *.pidb 58 | *.log 59 | *.scc 60 | 61 | # OS generated files # 62 | .DS_Store* 63 | Icon? 64 | 65 | # Visual C++ cache files 66 | ipch/ 67 | *.aps 68 | *.ncb 69 | *.opensdf 70 | *.sdf 71 | *.cachefile 72 | 73 | # Visual Studio profiler 74 | *.psess 75 | *.vsp 76 | *.vspx 77 | 78 | # Guidance Automation Toolkit 79 | *.gpState 80 | 81 | # ReSharper is a .NET coding add-in 82 | _ReSharper*/ 83 | *.[Rr]e[Ss]harper 84 | 85 | # TeamCity is a build add-in 86 | _TeamCity* 87 | 88 | # DotCover is a Code Coverage Tool 89 | *.dotCover 90 | 91 | # NCrunch 92 | *.ncrunch* 93 | .*crunch*.local.xml 94 | 95 | # Installshield output folder 96 | [Ee]xpress/ 97 | 98 | # DocProject is a documentation generator add-in 99 | DocProject/buildhelp/ 100 | DocProject/Help/*.HxT 101 | DocProject/Help/*.HxC 102 | DocProject/Help/*.hhc 103 | DocProject/Help/*.hhk 104 | DocProject/Help/*.hhp 105 | DocProject/Help/Html2 106 | DocProject/Help/html 107 | 108 | # Click-Once directory 109 | publish/ 110 | 111 | # Publish Web Output 112 | *.Publish.xml 113 | 114 | # Windows Azure Build Output 115 | csx 116 | *.build.csdef 117 | 118 | # Windows Store app package directory 119 | AppPackages/ 120 | 121 | # Others 122 | *.Cache 123 | ClientBin/ 124 | [Ss]tyle[Cc]op.* 125 | ~$* 126 | *~ 127 | *.dbmdl 128 | *.[Pp]ublish.xml 129 | *.pfx 130 | *.publishsettings 131 | modulesbin/ 132 | tempbin/ 133 | 134 | # EPiServer Site file (VPP) 135 | AppData/ 136 | 137 | # RIA/Silverlight projects 138 | Generated_Code/ 139 | 140 | # Backup & report files from converting an old project file to a newer 141 | # Visual Studio version. Backup files are not needed, because we have git ;-) 142 | _UpgradeReport_Files/ 143 | Backup*/ 144 | UpgradeLog*.XML 145 | UpgradeLog*.htm 146 | 147 | # vim 148 | *.txt~ 149 | *.swp 150 | *.swo 151 | 152 | # Temp files when opening LibreOffice on ubuntu 153 | .~lock.* 154 | 155 | # svn 156 | .svn 157 | 158 | # CVS - Source Control 159 | **/CVS/ 160 | 161 | # Remainings from resolving conflicts in Source Control 162 | *.orig 163 | 164 | # SQL Server files 165 | **/App_Data/*.mdf 166 | **/App_Data/*.ldf 167 | **/App_Data/*.sdf 168 | 169 | 170 | #LightSwitch generated files 171 | GeneratedArtifacts/ 172 | _Pvt_Extensions/ 173 | ModelManifest.xml 174 | 175 | # ========================= 176 | # Windows detritus 177 | # ========================= 178 | 179 | # Windows image file caches 180 | Thumbs.db 181 | ehthumbs.db 182 | 183 | # Folder config file 184 | Desktop.ini 185 | 186 | # Recycle Bin used on file shares 187 | $RECYCLE.BIN/ 188 | 189 | # Mac desktop service store files 190 | .DS_Store 191 | 192 | # SASS Compiler cache 193 | .sass-cache 194 | 195 | # Visual Studio 2014 CTP 196 | **/*.sln.ide 197 | 198 | # Visual Studio temp something 199 | .vs/ 200 | 201 | # dotnet stuff 202 | project.lock.json 203 | 204 | # VS 2015+ 205 | *.vc.vc.opendb 206 | *.vc.db 207 | 208 | # Rider 209 | .idea/ 210 | 211 | # Visual Studio Code 212 | .vscode/ 213 | 214 | # Output folder used by Webpack or other FE stuff 215 | **/node_modules/* 216 | **/wwwroot/* 217 | 218 | # SpecFlow specific 219 | *.feature.cs 220 | *.feature.xlsx.* 221 | *.Specs_*.html 222 | 223 | ##### 224 | # End of core ignore list, below put you custom 'per project' settings (patterns or path) 225 | ##### -------------------------------------------------------------------------------- /gui/src/Debugger.cpp: -------------------------------------------------------------------------------- 1 | #include "Debugger.hpp" 2 | #include "Gui.hpp" 3 | #include "portaudio.h" 4 | 5 | using namespace mahi::gui; 6 | 7 | void Debugger::update() 8 | { 9 | static double nxtTime = -1; 10 | static double cpuTotal = 0; 11 | static double cpuSession = 0; 12 | static std::size_t ram = 0; 13 | static auto glVer = glGetString(GL_VERSION); 14 | static auto vendor = glGetString(GL_VENDOR); 15 | static auto renderer = glGetString(GL_RENDERER); 16 | static float fps = 0; 17 | 18 | 19 | if (ImGui::GetTime() > nxtTime) { 20 | fps = ImGui::GetIO().Framerate; 21 | cpuTotal = mahi::util::cpu_usage_process(); 22 | cpuSession = gui.device.session ? gui.device.session->getCpuLoad() : 0; 23 | ram = mahi::util::ram_used_process(); 24 | nxtTime += 1.0; 25 | } 26 | 27 | if (show) 28 | { 29 | ImGui::Begin("Debug Info", &show, ImGuiWindowFlags_AlwaysAutoResize); 30 | // auto info = Debug::getInfo(); 31 | ImGui::Text("Signal Count: "); 32 | ImGui::SameLine(); 33 | ImGui::Text("%d", tact::Signal::count()); 34 | ImGui::Text("Max Voices: "); 35 | ImGui::SameLine(); 36 | ImGui::Text("%d", SYNTACTS_MAX_VOICES); 37 | #ifdef SYNTACTS_USE_POOL 38 | ImGui::Text("Pool Capacity: "); 39 | ImGui::SameLine(); 40 | ImGui::Text("%d", tact::Signal::pool().blocksTotal()); 41 | ImGui::Text("Pool Block Size "); 42 | ImGui::SameLine(); 43 | ImGui::Text("%d (%d total)", SYNTACTS_POOL_BLOCK_SIZE, SYNTACTS_POOL_BLOCK_SIZE * SYNTACTS_POOL_MAX_BLOCKS); 44 | #endif 45 | ImGui::Text("ASIO Support: "); 46 | ImGui::SameLine(); 47 | #ifdef PA_USE_ASIO 48 | ImGui::Text("True"); 49 | #else 50 | ImGui::Text("False"); 51 | #endif 52 | ImGui::Text("Pointer Type: "); 53 | ImGui::SameLine(); 54 | #ifdef SYNTACTS_USE_SHARED_PTR 55 | ImGui::Text("std::shared_ptr"); 56 | #else 57 | ImGui::Text("std::unique_ptr"); 58 | #endif 59 | ImGui::Text("Library Directory: "); 60 | ImGui::SameLine(); 61 | if (ImGui::TintedButton(tact::Library::getLibraryDirectory().c_str(), {0,0,0,0})) 62 | mahi::gui::open_folder(tact::Library::getLibraryDirectory()); 63 | ImGui::Text("GUI Directory: "); 64 | ImGui::SameLine(); 65 | if (ImGui::TintedButton(gui.saveDir().c_str(), {0,0,0,0})) 66 | mahi::gui::open_folder(gui.saveDir()); 67 | 68 | ImGui::Separator(); 69 | 70 | ImGui::Text("Framerate: "); 71 | ImGui::SameLine(); 72 | ImGui::Text("%.2f FPS", fps); 73 | ImGui::Text("Verts / Tris: "); 74 | ImGui::SameLine(); 75 | ImGui::Text("%d / %d", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices / 3); 76 | ImGui::Text("RAM Usage: "); 77 | ImGui::SameLine(); ImGui::Text("%d MB", ram / 1000000); 78 | ImGui::Text("CPU Load: "); ImGui::SameLine(); ImGui::Text("%.2f %%", cpuTotal); 79 | ImGui::Text("Session Load: "); ImGui::SameLine(); ImGui::Text("%.2f %%", cpuSession * 100); 80 | ImGui::Separator(); 81 | ImGui::Text("Operating System: "); 82 | ImGui::SameLine(); 83 | ImGui::Text("%s %s", mahi::util::os_name().c_str(), mahi::util::os_version().c_str()); 84 | ImGui::Text("Syntacts Version: "); 85 | ImGui::SameLine(); 86 | ImGui::Text(tact::syntactsVersion().c_str()); 87 | ImGui::Text("PortAudio Version: "); 88 | ImGui::SameLine(); 89 | ImGui::Text(Pa_GetVersionInfo()->versionText); 90 | ImGui::Text("ImGui Version: "); 91 | ImGui::SameLine(); 92 | ImGui::Text(ImGui::GetVersion()); 93 | ImGui::Text("GLFW Version: "); 94 | ImGui::SameLine(); 95 | ImGui::Text(glfwGetVersionString()); 96 | ImGui::Text("OpenGL Version: "); 97 | ImGui::SameLine(); 98 | ImGui::Text("%s\n",glVer); 99 | ImGui::Text("GPU: "); 100 | ImGui::SameLine(); 101 | ImGui::Text("%s\n",renderer); 102 | ImGui::PushStyleColor(ImGuiCol_Button, Reds::FireBrick); 103 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, Reds::Salmon); 104 | ImGui::PushStyleColor(ImGuiCol_ButtonActive, Reds::LightSalmon); 105 | if (ImGui::Button("Report Issue", {-1, 0})) 106 | mahi::gui::open_url("https://github.com/mahilab/Syntacts/issues"); 107 | ImGui::PopStyleColor(3); 108 | ImGui::End(); 109 | } 110 | } -------------------------------------------------------------------------------- /src/Tact/Util.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace tact { 6 | 7 | namespace { 8 | std::chrono::time_point g_tic, g_toc; 9 | } 10 | 11 | void sleep(double seconds, double max) { 12 | seconds = std::abs(seconds); 13 | max = std::abs(max); 14 | if (seconds != INF) 15 | std::this_thread::sleep_for(std::chrono::nanoseconds((std::size_t)(seconds * 1000000000))); 16 | else 17 | std::this_thread::sleep_for(std::chrono::nanoseconds((std::size_t)(max * 1000000000))); 18 | } 19 | 20 | 21 | void tic() { 22 | g_tic = std::chrono::high_resolution_clock::now(); 23 | } 24 | 25 | double toc() { 26 | g_toc = std::chrono::high_resolution_clock::now(); 27 | auto ns = std::chrono::duration_cast(g_toc - g_tic).count(); 28 | return ns / 1000000000.0; 29 | } 30 | 31 | const std::string& signalName(const Signal& signal) { 32 | auto id = signal.typeId(); 33 | static std::string unkown = "Unkown"; 34 | static std::unordered_map names = { 35 | // General.hpp 36 | {typeid(Scalar), "Scalar"}, 37 | {typeid(Time), "Time"}, 38 | {typeid(Ramp), "Ramp"}, 39 | {typeid(Noise), "Noise"}, 40 | {typeid(Expression), "Expression"}, 41 | {typeid(PolyBezier), "PolyBezier"}, 42 | {typeid(Samples), "Samples"}, 43 | // Operator.hpp 44 | {typeid(Sum), "Sum"}, 45 | {typeid(Product), "Product"}, 46 | // Sequence.hpp 47 | {typeid(Sequence), "Sequence"}, 48 | // Oscillator.hpp 49 | {typeid(Sine), "Sine"}, 50 | {typeid(Square), "Square"}, 51 | {typeid(Saw), "Saw"}, 52 | {typeid(Triangle), "Triangle"}, 53 | {typeid(Pwm), "PWM"}, 54 | // Envelope.hpp 55 | {typeid(Envelope), "Envelope"}, 56 | {typeid(KeyedEnvelope), "Keyed Envelope"}, 57 | {typeid(ASR), "ASR"}, 58 | {typeid(ADSR), "ADSR"}, 59 | {typeid(ExponentialDecay), "Exponential Decay"}, 60 | {typeid(SignalEnvelope), "Signal Envelope"}, 61 | // Process.hpp 62 | {typeid(Repeater), "Repeater"}, 63 | {typeid(Stretcher), "Stretcher"}, 64 | {typeid(Reverser), "Reverser"}}; 65 | if (names.count(id)) 66 | return names[id]; 67 | else 68 | return unkown; 69 | } 70 | 71 | void recurseSignalPriv(const Signal& sig, std::function func, int depth) { 72 | auto id = sig.typeId(); 73 | func(sig, depth); 74 | if (id == typeid(Sum)) { 75 | recurseSignalPriv(sig.getAs()->lhs,func,depth+1); 76 | recurseSignalPriv(sig.getAs()->rhs,func,depth+1); 77 | } 78 | else if (id == typeid(Product)) { 79 | recurseSignalPriv(sig.getAs()->lhs,func,depth+1); 80 | recurseSignalPriv(sig.getAs()->rhs,func,depth+1); 81 | } 82 | else if (id == typeid(Sequence)) { 83 | auto seq = sig.getAs(); 84 | int K = seq->keyCount(); 85 | for (int k = 0; k < K; ++k) 86 | recurseSignalPriv(seq->getKey(k).signal,func,depth+1); 87 | } 88 | else if (id == typeid(Repeater)) 89 | recurseSignalPriv(sig.getAs()->signal,func,depth+1); 90 | else if (id == typeid(Stretcher)) 91 | recurseSignalPriv(sig.getAs()->signal,func,depth+1); 92 | else if (id == typeid(Reverser)) 93 | recurseSignalPriv(sig.getAs()->signal,func,depth+1); 94 | else if (id == typeid(Sine)) 95 | recurseSignalPriv(sig.getAs()->x,func,depth+1); 96 | else if (id == typeid(Square)) 97 | recurseSignalPriv(sig.getAs()->x,func,depth+1); 98 | else if (id == typeid(Saw)) 99 | recurseSignalPriv(sig.getAs()->x,func,depth+1); 100 | else if (id == typeid(Triangle)) 101 | recurseSignalPriv(sig.getAs()->x,func,depth+1); 102 | else if (id == typeid(SignalEnvelope)) 103 | recurseSignalPriv(sig.getAs()->signal,func,depth+1); 104 | } 105 | 106 | /// Recurse a signal for embedded signals and calls func on each 107 | void recurseSignal(const Signal& signal, std::function func) { 108 | recurseSignalPriv(signal, func, 0); 109 | } 110 | 111 | const std::string& syntactsVersion() { 112 | static std::string ver = std::to_string(SYNTACTS_VERSION_MAJOR) + "." 113 | + std::to_string(SYNTACTS_VERSION_MINOR) + "." 114 | + std::to_string(SYNTACTS_VERSION_PATCH); 115 | return ver; 116 | } 117 | 118 | }; -------------------------------------------------------------------------------- /gui/src/Palette.cpp: -------------------------------------------------------------------------------- 1 | #include "Palette.hpp" 2 | #include "Nodes.hpp" 3 | #include "Gui.hpp" 4 | #include 5 | 6 | using namespace mahi::gui; 7 | 8 | /// Returns the name of a Syntacts signal 9 | const std::string &paletteName(PItem id) 10 | { 11 | static std::unordered_map names = { 12 | {PItem::Unknown, "Unknown"}, 13 | {PItem::Time, "Time"}, 14 | {PItem::Scalar, "Scalar"}, 15 | {PItem::Ramp, "Ramp"}, 16 | {PItem::Noise, "Noise"}, 17 | {PItem::Expression, "Expression"}, 18 | {PItem::Sum, "Sum"}, 19 | {PItem::Product, "Product"}, 20 | {PItem::Repeater, "Repeater"}, 21 | {PItem::Stretcher, "Stretcher"}, 22 | {PItem::Reverser, "Reverser"}, 23 | {PItem::Sequencer, "Sequencer"}, 24 | {PItem::Sine, "Sine"}, 25 | {PItem::Square, "Square"}, 26 | {PItem::Saw, "Saw"}, 27 | {PItem::Triangle, "Triangle"}, 28 | {PItem::FM, "FM"}, 29 | {PItem::Chirp, "Chirp"}, 30 | {PItem::Pwm, "PWM"}, 31 | {PItem::Envelope, "Envelope"}, 32 | {PItem::ASR, "ASR"}, 33 | {PItem::ADSR, "ADSR"}, 34 | {PItem::ExponentialDecay, "Exponential Decay"}, 35 | {PItem::KeyedEnvelope, "Keyed Envelope"}, 36 | {PItem::SignalEnvelope, "Signal Envelope"}, 37 | {PItem::PolyBezier, "PolyBezier"}}; 38 | if (names.count(id)) 39 | return names[id]; 40 | else 41 | return names[PItem::Unknown]; 42 | } 43 | 44 | const std::string& paletteDescription(PItem id) { 45 | static std::unordered_map desc = { 46 | {PItem::Unknown, "Hmm, we don't know what this does ..."}, 47 | {PItem::Time, "Creates a sample equal to the sample time"}, 48 | {PItem::Scalar, "Creates a constant sample value"}, 49 | {PItem::Ramp, "Creates a sample that linearly increases or decreases with time"}, 50 | {PItem::Noise, "Creates white noise"}, 51 | {PItem::Expression, "Creates a sample by evaluating a mathematical expression"}, 52 | {PItem::Sum, "Sums two or more Signals"}, 53 | {PItem::Product, "Multiplies two or more Signals"}, 54 | {PItem::Repeater, "Repeats a Signal a specified number of times"}, 55 | {PItem::Stretcher, "Stretches or compresses a Signal in time"}, 56 | {PItem::Reverser, "Reverses a Signal"}, 57 | {PItem::Sequencer, "Sequences multiple Signals in time"}, 58 | {PItem::Sine, "Creates a basic sine wave oscillator"}, 59 | {PItem::Square, "Creates a basic square wave oscillator"}, 60 | {PItem::Saw, "Creates a basic saw wave oscillator"}, 61 | {PItem::Triangle, "Creates a basic triangle wave oscillator"}, 62 | {PItem::FM, "Creates a frequency modulated oscillator"}, 63 | {PItem::Chirp, "Creates an oscillator with an linearly increasing or decreasing frequency"}, 64 | {PItem::Pwm, "Creates a pulse width modulated oscillator"}, 65 | {PItem::Envelope, "Creates a basic envelope"}, 66 | {PItem::ASR, "Creates an Attack, Sustain, Release envelope"}, 67 | {PItem::ADSR, "Creates an Attack, Decay, Sustain, Release envelope"}, 68 | {PItem::ExponentialDecay, "Creates an exponentially decaying envelope"}, 69 | {PItem::KeyedEnvelope, "Creates a envelope from a series of key frame times, amplitudes, and Curves"}, 70 | {PItem::SignalEnvelope, "Creates an envelope from a Signal"}, 71 | {PItem::PolyBezier, "Creates an envelope from a cubic bezier curve"}}; 72 | if (desc.count(id)) 73 | return desc[id]; 74 | else 75 | return desc[PItem::Unknown]; 76 | } 77 | 78 | void Palette::update() 79 | { 80 | auto avail = ImGui::GetContentRegionAvail(); 81 | ImGui::PushStyleColor(ImGuiCol_ChildBg, {0,0,0,0}); 82 | ImGui::BeginChild("PalleteList", ImVec2(0, avail.y)); 83 | static std::vector>> signals = { 84 | {"Oscillators", {PItem::Sine, PItem::Square, PItem::Saw, PItem::Triangle, PItem::Chirp, PItem::FM, PItem::Pwm, PItem::Noise}}, 85 | {"Envelopes", {PItem::Envelope, PItem::KeyedEnvelope, PItem::ASR, PItem::ADSR, PItem::ExponentialDecay, PItem::PolyBezier, PItem::SignalEnvelope}}, 86 | {"Processes", {PItem::Sum, PItem::Product, PItem::Repeater, PItem::Stretcher, PItem::Reverser, PItem::Sequencer}}, 87 | {"General", {PItem::Expression, PItem::Ramp, PItem::Scalar}}}; 88 | for (auto §ion : signals) 89 | { 90 | if (!ImGui::CollapsingHeader(section.first.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) 91 | continue; 92 | for (auto &pItem : section.second) 93 | { 94 | ImGui::Selectable(paletteName(pItem).c_str(), false); 95 | PaletteSource(pItem); 96 | gui.status.showTooltip(paletteDescription(pItem)); 97 | } 98 | } 99 | ImGui::EndChild(); 100 | ImGui::PopStyleColor(); 101 | } -------------------------------------------------------------------------------- /include/Tact/Spatializer.hpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 Mechatronics and Haptic Interfaces Lab 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | // 23 | // Author(s): Evan Pezent (epezent@rice.edu) 24 | 25 | #pragma once 26 | 27 | #include 28 | 29 | namespace tact 30 | { 31 | 32 | /// Syntacts Spatializer interface. 33 | class SYNTACTS_API Spatializer { 34 | public: 35 | 36 | // 2D Spatializer point 37 | struct Point { 38 | double x,y; 39 | }; 40 | 41 | /// Constructor. 42 | Spatializer(Session* session = nullptr); 43 | /// Destructor. 44 | ~Spatializer(); 45 | 46 | /// Binds this Spatializer to a Session. 47 | void bind(Session* session); 48 | /// Unbinds the Spatializer from its current Session. 49 | void unbind(); 50 | 51 | /// Set the position of a channel. The channel will be added if it's not already in the Spatializer. 52 | void setPosition(int channel, double x, double y = 0); 53 | /// Set the position of a channel. The channel will be added if it's not already in the Spatializer. 54 | void setPosition(int channel, const Point& p); 55 | /// Gets the position of a channel if it is in the Spatializer. 56 | Point getPosition(int channel) const; 57 | 58 | /// Set the Spatialier target position. 59 | void setTarget(double x, double y = 0); 60 | /// Set the Spatialier target position. 61 | void setTarget(const Point& p); 62 | /// Get the Spatializer target position. 63 | const Point& getTarget() const; 64 | /// Set the Spatializer target radius. 65 | void setRadius(double r); 66 | /// Get the Spatializer target radius. 67 | double getRadius() const; 68 | /// Set the Spatializer target roll-off method. 69 | void setRollOff(Curve rollOff); 70 | /// Get the Spatializer target roll-off method. 71 | Curve getRollOff() const; 72 | /// Enable Spatializer wrapping for one or both axes. A value of zero disables wrapping. 73 | void setWrap(double xInterval, double yInterval); 74 | /// Enable Spatializer wrapping for one or both axes. A value of zero disables wrapping. 75 | void setWrap(const Point& wrapInterval); 76 | /// Get Spatializer wrapping intervals. 0s indicate wrapping is disabled. 77 | const Point& getWrap() const; 78 | 79 | /// Quickly create a grid of channels. 80 | bool createGrid(int rows, int cols); 81 | /// Remove all channels from the Spatializer. 82 | void clear(); 83 | /// Remove a channel from the Spatializer. 84 | void remove(int channel); 85 | /// Get the number of channels in the Spatializer. 86 | int getChannelCount() const; 87 | /// Returns true if a channel is in the Spatializer. 88 | bool hasChannel(int channel) const; 89 | /// Get all the channels in the Spatializer. 90 | std::vector getChannels() const; 91 | 92 | /// Play a Signal on the Spatializer. 93 | void play(Signal signal); 94 | /// Stop all Signals playing in the Spatializer. 95 | void stop(); 96 | 97 | /// Set the global volume of the Spatializer. 98 | void setVolume(double volume); 99 | /// Get the global volume of the Spatializer. 100 | double getVolume() const; 101 | 102 | /// Set the global pitch of the Spatializer. 103 | void setPitch(double pitch); 104 | /// Get the global pitch of the Spatializer. 105 | double getPitch() const; 106 | 107 | /// Enable/disable automatic updating of pitch/volume of all channels when Spatializer target or channels change (enabled by default). 108 | void autoUpdate(bool enable); 109 | /// Explicitly update the pitch/volume of all channels in the Spatializer. 110 | void update(); 111 | 112 | private: 113 | Session* m_session; 114 | Point m_target; 115 | double m_radius; 116 | double m_volume; 117 | double m_pitch; 118 | Curve m_rollOff; 119 | bool m_autoUpdate; 120 | Point m_wrapInterval; 121 | std::map m_positions; 122 | }; 123 | 124 | } // namespace tact 125 | --------------------------------------------------------------------------------