gautrais пре 4 година
комит
d13c002b52
60 измењених фајлова са 10181 додато и 0 уклоњено
  1. 34 0
      CMakeLists.txt
  2. 292 0
      CMakeLists.txt.user
  3. 256 0
      conf/conf.conf
  4. 264 0
      conf/conf2.conf
  5. 249 0
      conf/conf3.conf
  6. 196 0
      include/AbsInput.h
  7. 90 0
      include/Application.h
  8. 55 0
      include/Button.h
  9. 71 0
      include/Command.h
  10. 88 0
      include/Config.h
  11. 48 0
      include/Controller.h
  12. 29 0
      include/IInputEventListener.h
  13. 202 0
      include/InputAction.h
  14. 56 0
      include/InputDefinition.h
  15. 224 0
      include/MidiMessage.h
  16. 89 0
      include/MidiPort.h
  17. 119 0
      include/Operations.h
  18. 39 0
      include/Pad.h
  19. 44 0
      include/PadConfiguration.h
  20. 118 0
      include/PadDefinition.h
  21. 58 0
      include/PadManager.h
  22. 73 0
      include/PadSelection.h
  23. 206 0
      include/Socket.h
  24. 30 0
      include/error.h
  25. 55 0
      include/utils.h
  26. 95 0
      main.cpp
  27. 759 0
      pads/apc.json
  28. 9 0
      simplemidi.h
  29. 12 0
      src/AbsInput.cpp
  30. 330 0
      src/Application.cpp
  31. 9 0
      src/Button.cpp
  32. 137 0
      src/Command.cpp
  33. 203 0
      src/Config.cpp
  34. 9 0
      src/IInputEventListener.cpp
  35. 8 0
      src/InputAction.cpp
  36. 40 0
      src/InputDefinition.cpp
  37. 52 0
      src/MidiMessage.cpp
  38. 112 0
      src/MidiPort.cpp
  39. 104 0
      src/Operations.cpp
  40. 112 0
      src/Pad.cpp
  41. 158 0
      src/PadConfiguration.cpp
  42. 334 0
      src/PadDefinition.cpp
  43. 141 0
      src/PadManager.cpp
  44. 195 0
      src/PadSelection.cpp
  45. 252 0
      src/Socket.cpp
  46. 123 0
      src/commands.cpp
  47. 261 0
      src/utils.cpp
  48. 139 0
      ui/GtkButtonGrid.py
  49. 53 0
      ui/GtkSelectionList.py
  50. 135 0
      ui/MidiConst.py
  51. 19 0
      ui/OperationBox.py
  52. 686 0
      ui/application.py
  53. 4 0
      ui/config.py
  54. 54 0
      ui/main.py
  55. 313 0
      ui/pad.py
  56. 152 0
      ui/pad_config.py
  57. 122 0
      ui/padbuilder.py
  58. 48 0
      ui/padmanager.py
  59. 196 0
      ui/simplemidi.py
  60. 1820 0
      ui/ui.glade

+ 34 - 0
CMakeLists.txt

@@ -0,0 +1,34 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(simplemidi LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(src main.cpp
+    src/MidiPort.cpp
+    src/AbsInput.cpp
+    src/utils.cpp
+    src/Application.cpp
+    src/InputDefinition.cpp
+    src/IInputEventListener.cpp
+    src/Pad.cpp
+    src/Button.cpp
+    src/commands.cpp
+    src/InputAction.cpp
+    src/MidiMessage.cpp
+    src/PadSelection.cpp
+    src/Operations.cpp
+    src/PadConfiguration.cpp
+    src/Command.cpp
+    src/Socket.cpp
+    src/PadManager.cpp
+    src/Config.cpp
+    src/PadDefinition.cpp)
+
+
+add_executable(simplemidi ${src})
+include_directories(include)
+include_directories(/usr/include/rtmidi)
+target_link_libraries(simplemidi /usr/lib/libjsoncpp.so)
+target_link_libraries(simplemidi /usr/lib/librtmidi.so)

+ 292 - 0
CMakeLists.txt.user

@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorProject>
+<!-- Written by QtCreator 4.13.3, 2020-12-12T22:19:44. -->
+<qtcreator>
+ <data>
+  <variable>EnvironmentId</variable>
+  <value type="QByteArray">{c3eb6cc5-b1ea-4f6d-b536-cee3ab505721}</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.ActiveTarget</variable>
+  <value type="int">0</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.EditorSettings</variable>
+  <valuemap type="QVariantMap">
+   <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
+   <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
+   <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
+    <value type="QString" key="language">Cpp</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">qt2</value>
+    </valuemap>
+   </valuemap>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
+    <value type="QString" key="language">QmlJS</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
+    </valuemap>
+   </valuemap>
+   <value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
+   <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
+   <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
+   <value type="int" key="EditorConfiguration.IndentSize">4</value>
+   <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
+   <value type="int" key="EditorConfiguration.MarginColumn">80</value>
+   <value type="bool" key="EditorConfiguration.MouseHiding">true</value>
+   <value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
+   <value type="int" key="EditorConfiguration.PaddingMode">1</value>
+   <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
+   <value type="bool" key="EditorConfiguration.ShowMargin">false</value>
+   <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
+   <value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
+   <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
+   <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
+   <value type="int" key="EditorConfiguration.TabSize">8</value>
+   <value type="bool" key="EditorConfiguration.UseGlobal">true</value>
+   <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
+   <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
+   <value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
+   <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
+   <value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
+   <value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
+   <value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.PluginSettings</variable>
+  <valuemap type="QVariantMap">
+   <valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
+    <value type="bool" key="AutoTest.Framework.Boost">true</value>
+    <value type="bool" key="AutoTest.Framework.Catch">true</value>
+    <value type="bool" key="AutoTest.Framework.GTest">true</value>
+    <value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
+    <value type="bool" key="AutoTest.Framework.QtTest">true</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
+   <value type="int" key="AutoTest.RunAfterBuild">0</value>
+   <value type="bool" key="AutoTest.UseGlobal">true</value>
+   <valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
+   <value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
+   <value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.Questionable</value>
+   <valuemap type="QVariantMap" key="ClangTools">
+    <value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
+    <value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
+    <value type="int" key="ClangTools.ParallelJobs">2</value>
+    <valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
+    <valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
+    <valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
+    <value type="bool" key="ClangTools.UseGlobalSettings">true</value>
+   </valuemap>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.0</variable>
+  <valuemap type="QVariantMap">
+   <value type="QString" key="DeviceType">Desktop</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{ba5119cf-0678-4e46-9f2b-7ada5c0aa529}</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
+    <value type="QString" key="CMake.Initial.Parameters">-GNinja
+-DCMAKE_BUILD_TYPE:String=Debug
+-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
+-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX}
+-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C}
+-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx}</value>
+    <value type="int" key="EnableQmlDebugging">2</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/fanch/Programmation/simplemidi/build/debug</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments"></value>
+      <valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
+       <value type="QString">all</value>
+      </valuelist>
+      <value type="QString" key="CMakeProjectManager.MakeStep.CMakeArguments"></value>
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments"></value>
+      <valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
+       <value type="QString">clean</value>
+      </valuelist>
+      <value type="QString" key="CMakeProjectManager.MakeStep.CMakeArguments"></value>
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
+    <value type="QString" key="CMake.Initial.Parameters">-GNinja
+-DCMAKE_BUILD_TYPE:String=Release
+-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
+-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX}
+-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C}
+-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx}</value>
+    <value type="int" key="EnableQmlDebugging">2</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/fanch/Programmation/simplemidi/build/release</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments"></value>
+      <valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
+       <value type="QString">all</value>
+      </valuelist>
+      <value type="QString" key="CMakeProjectManager.MakeStep.CMakeArguments"></value>
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="QString" key="CMakeProjectManager.MakeStep.AdditionalArguments"></value>
+      <valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
+       <value type="QString">clean</value>
+      </valuelist>
+      <value type="QString" key="CMakeProjectManager.MakeStep.CMakeArguments"></value>
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">2</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
+    <value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
+    <value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
+    <valuelist type="QVariantList" key="Analyzer.Perf.Events">
+     <value type="QString">cpu-cycles</value>
+    </valuelist>
+    <valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
+    <value type="int" key="Analyzer.Perf.Frequency">250</value>
+    <valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
+     <value type="QString">-e</value>
+     <value type="QString">cpu-cycles</value>
+     <value type="QString">--call-graph</value>
+     <value type="QString">dwarf,4096</value>
+     <value type="QString">-F</value>
+     <value type="QString">250</value>
+    </valuelist>
+    <value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
+    <value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
+    <value type="int" key="Analyzer.Perf.StackSize">4096</value>
+    <value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
+    <value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
+    <value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
+    <value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
+    <value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
+    <value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
+    <value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
+    <value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
+    <value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
+    <value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
+    <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
+    <value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
+    <value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
+    <value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
+     <value type="int">0</value>
+     <value type="int">1</value>
+     <value type="int">2</value>
+     <value type="int">3</value>
+     <value type="int">4</value>
+     <value type="int">5</value>
+     <value type="int">6</value>
+     <value type="int">7</value>
+     <value type="int">8</value>
+     <value type="int">9</value>
+     <value type="int">10</value>
+     <value type="int">11</value>
+     <value type="int">12</value>
+     <value type="int">13</value>
+     <value type="int">14</value>
+    </valuelist>
+    <valuelist type="QVariantList" key="CustomOutputParsers"/>
+    <value type="int" key="PE.EnvironmentAspect.Base">2</value>
+    <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">simplemidi</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeRunConfiguration.simplemidi</value>
+    <value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">simplemidi</value>
+    <value type="QString" key="RunConfiguration.Arguments"></value>
+    <value type="bool" key="RunConfiguration.Arguments.multi">false</value>
+    <value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
+    <value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
+    <value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
+    <value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
+    <value type="QString" key="RunConfiguration.WorkingDirectory">/home/fanch/Programmation/simplemidi</value>
+    <value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/fanch/Programmation/simplemidi/build/debug</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.TargetCount</variable>
+  <value type="int">1</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
+  <value type="int">22</value>
+ </data>
+ <data>
+  <variable>Version</variable>
+  <value type="int">22</value>
+ </data>
+</qtcreator>

+ 256 - 0
conf/conf.conf

@@ -0,0 +1,256 @@
+{
+    "name": "APC Mini",
+    "description": "",
+    "pad": "APC Mini",
+    "selections": {
+        "a": {
+            "operations": [
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        1,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                "noteon",
+                -1,
+                -1,
+                1
+            ],
+            "locked": []
+        },
+        "b": {
+            "operations": [
+                {
+                    "type": "REORDER",
+                    "offset": 0
+                },
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        2,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                "noteon",
+                -1,
+                -1,
+                3
+            ],
+            "locked": []
+        },
+        "c": {
+            "operations": [
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        3,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                "noteon",
+                -1,
+                -1,
+                1
+            ],
+            "locked": []
+        },
+        "d": {
+            "operations": [
+                {
+                    "type": "REORDER",
+                    "offset": 0
+                },
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        4,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                "noteon",
+                -1,
+                -1,
+                3
+            ],
+            "locked": []
+        },
+        "e": {
+            "operations": [
+                {
+                    "type": "REORDER",
+                    "offset": 0
+                },
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        4,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [],
+            "locked": []
+        }
+    },
+    "matrix": [
+        "a",
+        "a",
+        "a",
+        "a",
+        "b",
+        "b",
+        "b",
+        "b",
+        null,
+        null,
+        "a",
+        "a",
+        "a",
+        "a",
+        "b",
+        "b",
+        "b",
+        "b",
+        null,
+        null,
+        "a",
+        "a",
+        "a",
+        "a",
+        "b",
+        "b",
+        "b",
+        "b",
+        null,
+        null,
+        "a",
+        "a",
+        "a",
+        "a",
+        "b",
+        "b",
+        "b",
+        "b",
+        null,
+        null,
+        "d",
+        "d",
+        "d",
+        "d",
+        "c",
+        "c",
+        "c",
+        "c",
+        null,
+        null,
+        "d",
+        "d",
+        "d",
+        "d",
+        "c",
+        "c",
+        "c",
+        "c",
+        null,
+        null,
+        "d",
+        "d",
+        "d",
+        "d",
+        "c",
+        "c",
+        "c",
+        "c",
+        null,
+        null,
+        "d",
+        "d",
+        "d",
+        "d",
+        "c",
+        "c",
+        "c",
+        "c",
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        null
+    ]
+}

+ 264 - 0
conf/conf2.conf

@@ -0,0 +1,264 @@
+{
+    "name": "APC Mini",
+    "description": "",
+    "pad": "APC Mini",
+    "selections": {
+        "a": {
+            "operations": [
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        1,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                [
+                    "noteon",
+                    -1,
+                    -1,
+                    2
+                ]
+            ],
+            "locked": []
+        },
+        "b": {
+            "operations": [
+                {
+                    "type": "REORDER",
+                    "offset": 0
+                },
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        2,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                [
+                    "noteon",
+                    -1,
+                    -1,
+                    3
+                ]
+            ],
+            "locked": []
+        },
+        "c": {
+            "operations": [
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        3,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                [
+                    "noteon",
+                    -1,
+                    -1,
+                    1
+                ]
+            ],
+            "locked": []
+        },
+        "d": {
+            "operations": [
+                {
+                    "type": "REORDER",
+                    "offset": 0
+                },
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        4,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                [
+                    "noteon",
+                    -1,
+                    -1,
+                    3
+                ]
+            ],
+            "locked": []
+        },
+        "e": {
+            "operations": [
+                {
+                    "type": "REORDER",
+                    "offset": 0
+                },
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        4,
+                        0,
+                        0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [],
+            "locked": []
+        }
+    },
+    "matrix": [
+        "a",
+        "a",
+        "a",
+        "a",
+        "b",
+        "b",
+        "b",
+        "b",
+        null,
+        null,
+        "a",
+        "a",
+        "a",
+        "a",
+        "b",
+        "b",
+        "b",
+        "b",
+        null,
+        null,
+        "a",
+        "a",
+        "a",
+        "a",
+        "b",
+        "b",
+        "b",
+        "b",
+        null,
+        null,
+        "a",
+        "a",
+        null,
+        null,
+        "b",
+        "b",
+        "b",
+        "b",
+        null,
+        null,
+        "d",
+        "d",
+        "d",
+        "d",
+        "c",
+        "c",
+        "c",
+        "c",
+        null,
+        null,
+        "d",
+        "d",
+        "d",
+        "d",
+        "c",
+        "c",
+        "c",
+        "c",
+        null,
+        null,
+        "d",
+        "d",
+        "d",
+        "d",
+        "c",
+        "c",
+        "c",
+        "c",
+        null,
+        null,
+        "d",
+        "d",
+        "d",
+        "d",
+        "c",
+        "c",
+        "c",
+        "c",
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        "e",
+        null
+    ]
+}

+ 249 - 0
conf/conf3.conf

@@ -0,0 +1,249 @@
+{
+    "name": "",
+    "description": "",
+    "pad": "APC Mini",
+    "selections": {
+        "Pad": {
+            "operations": [
+                {
+                    "type": "RANGE",
+                    "range": [
+                        0,
+                        2,
+                        4,
+                        5,
+                        7,
+                        9,
+                        11
+                    ],
+                    "key": 60,
+                    "length": 12
+                },
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        4.0,
+                        0.0,
+                        0.0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                [
+                    "noteon",
+                    -1,
+                    -1,
+                    5
+                ]
+            ],
+            "locked": []
+        },
+        "Bells": {
+            "operations": [
+                {
+                    "type": "RANGE",
+                    "range": [
+                        0,
+                        2,
+                        4,
+                        5,
+                        7,
+                        9,
+                        11
+                    ],
+                    "key": 60,
+                    "length": 12
+                },
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        2.0,
+                        0.0,
+                        0.0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                [
+                    "noteon",
+                    -1,
+                    -1,
+                    1
+                ]
+            ],
+            "locked": []
+        },
+        "Lead": {
+            "operations": [
+                {
+                    "type": "RANGE",
+                    "range": [
+                        0,
+                        2,
+                        4,
+                        5,
+                        7,
+                        9,
+                        11
+                    ],
+                    "key": 60,
+                    "length": 12
+                },
+                {
+                    "type": "TRANSLATE",
+                    "values": [
+                        3.0,
+                        0.0,
+                        0.0
+                    ],
+                    "modes": [
+                        "FIXED",
+                        "RELATIVE",
+                        "RELATIVE"
+                    ]
+                }
+            ],
+            "init": [
+                [
+                    "noteon",
+                    -1,
+                    -1,
+                    3
+                ]
+            ],
+            "locked": []
+        }
+    },
+    "matrix": [
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        null,
+        null,
+        null,
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        null,
+        null,
+        null,
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        "Pad",
+        null,
+        null,
+        null,
+        "Lead",
+        "Lead",
+        "Lead",
+        "Lead",
+        "Lead",
+        "Lead",
+        "Lead",
+        null,
+        null,
+        null,
+        "Lead",
+        "Lead",
+        "Lead",
+        "Lead",
+        "Lead",
+        "Lead",
+        "Lead",
+        null,
+        null,
+        null,
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        null,
+        null,
+        null,
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        null,
+        null,
+        null,
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        "Bells",
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null
+    ]
+}

+ 196 - 0
include/AbsInput.h

@@ -0,0 +1,196 @@
+/*
+ * Input.h
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+#ifndef SRC_INPUT_H_
+#define SRC_INPUT_H_
+
+#include "utils.h"
+#include "InputAction.h"
+#include "MidiMessage.h"
+#include "IInputEventListener.h"
+#include "MidiPort.h"
+enum InputType { BUTTON, CONTROLLER};
+
+
+class PadDefinition;
+class Button;
+class Controller;
+class PadSelection;
+
+class AbsInput
+{
+    public:
+
+		static AbsInput* from_json(PadDefinition* p, Json::Value& v);
+
+		AbsInput(PadDefinition* p=NULL, InputType t=BUTTON){
+            type=t;
+            m_parent=p;
+            m_selection=NULL;
+        }
+        AbsInput(const AbsInput& b)
+        {
+            operator=(b);
+        }
+        virtual const AbsInput& operator=(const AbsInput& b)
+        {
+            type=b.type;
+            m_parent=b.m_parent;
+            m_selection=b.m_selection;
+            x=b.x;
+            y=b.y;
+            index=b.index;
+            realkey=b.realkey;
+            virtualkey=b.virtualkey;
+            realchannel=b.realchannel;
+            virtualchannel=b.virtualchannel;
+            realvel=b.realvel;
+            virtualvel=b.virtualvel;
+            m_actions = b.m_actions;
+            locked = b.locked;
+            return *this;
+        }
+        virtual ~AbsInput(){}
+
+        InputActionList* get_action(int i) {
+            if(i>=0 && i<(int)m_actions.size()) return m_actions[i];
+            return NULL;
+        }
+
+        InputActionList* get_action(const std::string& str) {
+            int size = m_actions.size();
+            for(int i=0; i<size; i++)
+            {
+                if(str==m_actions[i]->get_name())
+                    return m_actions[i];
+            }
+            return NULL;
+        }
+
+        MidiMessageList* get_message_action(int i) {
+            InputActionList* b = get_action(i);
+            return b?b->get_messages():NULL;
+        }
+
+        MidiMessageList* get_message_action(const std::string& i) {
+            InputActionList* b = get_action(i);
+            return b?b->get_messages():NULL;
+        }
+
+        virtual void on_input(double ts, MidiMessage* m)
+        {
+            printf("(%d, %d) : [%d, %d]\n", realchannel, realkey, x, y);
+        }
+
+        void send(MidiMessage* m){
+            get_output_port().send(*m);
+        }
+
+        void control(const std::string& s, bool force=false){
+            if(!force && is_locked()) return;
+            MidiMessageList* mm = get_message_action(s);
+            if(!mm) return;
+            int ss = mm->size();
+            for(int i=0; i<ss; i++)
+            {
+                MidiMessage* m = (*mm)[i];
+                switch(m->type)
+                {
+                    case MidiMessage::NOTE_OFF:
+                    case MidiMessage::NOTE_ON:
+                    case MidiMessage::CONTROLLER_CHANGE:
+                    {
+                        Note* note = dynamic_cast<Note*>(m)->copy();
+                        if(note->channel==255) note->channel=realchannel;
+                        if(note->key==255) note->key=realkey;
+                        if(note->velocity==255) note->velocity=realvel;
+                        MidiPortOut& port = get_control_port();
+                        port.send(*note);
+                        delete note;
+                    }
+                }
+            }
+        }
+
+        void control(MidiMessageList* mm, bool force=false){
+            int ss = mm->size();
+            for(int i=0; i<ss; i++)
+            {
+                MidiMessage* m = (*mm)[i];
+                switch(m->type)
+                {
+                    case MidiMessage::NOTE_OFF:
+                    case MidiMessage::NOTE_ON:
+                    case MidiMessage::CONTROLLER_CHANGE:
+                    {
+                        Note* note = dynamic_cast<Note*>(m)->copy();
+                        if(note->channel==255) note->channel=realchannel;
+                        if(note->key==255) note->key=realkey;
+                        if(note->velocity==255) note->velocity=realvel;
+                        MidiPortOut& port = get_control_port();
+                        port.send(*note);
+                        delete note;
+                    }
+                }
+            }
+        }
+
+        void control(int i, bool force=false){
+            if(!force && is_locked()) return;
+            MidiMessageList* m = get_message_action(i);
+            if(m) get_control_port().send(*m);
+        }
+
+
+
+
+
+        virtual MidiPortIn& get_input_port();
+        virtual MidiPortOut& get_control_port();
+        virtual MidiPortOut& get_output_port();
+
+        virtual void on_noteon(Button* b, double ts, MidiMessage* msg){}
+        virtual void on_noteoff(Button* b, double ts, MidiMessage* msg){}
+
+
+        bool is_locked() const {return locked!="";}
+        InputType type;
+        int x;
+        int y;
+        int index;
+
+        int realkey;
+        int virtualkey;
+
+        int realchannel;
+        int virtualchannel;
+
+        int realvel;
+        int virtualvel;
+
+        std::string locked;
+
+        PadSelection* get_selection() const {return m_selection;}
+        void    set_selection(PadSelection* p){m_selection=p;}
+
+    protected:
+        std::vector<InputActionList*>       m_actions;
+        std::vector<IInputEventListener*>   m_listeners;
+		PadDefinition*                      m_parent;
+        PadSelection*                       m_selection;
+
+
+		friend class PadDefinition;
+        friend class InputDefinition;
+
+};
+
+
+
+
+
+#endif /* SRC_INPUT_H_ */

+ 90 - 0
include/Application.h

@@ -0,0 +1,90 @@
+#ifndef __APPLICATION__H_
+#define __APPLICATION__H_
+
+#include <string>
+#include <vector>
+#include <json/json.h>
+
+#include "Command.h"
+class IAbsServer;
+class Pad;
+class ISocket;
+
+#define  SEND(__sock__,__msg__) {std::ostringstream os; os << __msg__ <<"\n";  (__sock__).write(os.str());}
+
+class Application;
+class Pad;
+
+
+class CommandLog : public std::ostringstream
+{
+	public:
+		CommandLog() : std::ostringstream(){ error=0; message="success"; }
+		virtual ~CommandLog(){}
+		void set(int err=0, const std::string& msg=""){
+			error=err;
+			message=msg;
+		}
+		void write(ISocket& sock, const std::string& append="");
+		int error;
+		std::string message;
+
+};
+
+
+
+typedef Command::CommandReturn (*CommandCallback)(Application&, Pad&, std::vector<Json::Value>&, CommandLog&);
+
+class CommandHandler
+{
+	public:
+		CommandHandler(const std::string& s, CommandCallback c, const std::string& h){
+			name=s;
+			command=c;
+			help=h;
+		}
+		virtual ~CommandHandler(){}
+		std::string		name;
+		std::string		help;
+		CommandCallback	command;
+};
+
+class Application
+{
+	public:
+											Application();
+		virtual								~Application();
+		void								process_next_command(int=1);
+		void								process();
+		Pad&								operator[](int);
+		Pad&								operator[](const std::string&);
+		Pad&								get_pad(int=-1);
+		Pad&								get_pad(const std::string& = "");
+		void								remove_pad(int);
+		void								remove_pad(const std::string&);
+		int									add_pad(Pad*);
+		int									add_pad(const std::string&);
+		bool								has_pad(const std::string&);
+		Pad&								set_pad(int);
+		Pad&								set_pad(const std::string&);
+		void								add_command(const std::string&, CommandCallback, const std::string&);
+		void								help(CommandLog&);
+		int									pad_count() const {return m_pads.size();}
+		bool								load_pad_configuration(const std::string&);
+		bool								load_pad_definition(const std::string&);
+		int									get_id(const std::string&);
+	protected:
+		Command::CommandReturn				_call(Command&, ISocket&);
+		void								_process_one();
+		void								_auto_load_pad();
+
+		std::vector<std::pair<int, Pad*> >	m_pads;
+		int									m_pad_last_id;
+		IAbsServer*							m_server;
+		Pad*								m_current;
+		int  								m_current_index;
+		std::vector<CommandHandler*>		m_callbacks;
+};
+
+
+#endif

+ 55 - 0
include/Button.h

@@ -0,0 +1,55 @@
+/*
+ * Button.h
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+#ifndef SRC_BUTTON_H_
+#define SRC_BUTTON_H_
+
+#include "AbsInput.h"
+
+/**
+ * @brief La classe qui représente les boutons du PAD
+ */
+
+class Button : public AbsInput
+{
+    public:
+		Button(PadDefinition* p=NULL) : AbsInput(p, BUTTON) {m_is_pressed=false;}
+        Button(const Button& b) : AbsInput(b.m_parent, b.type)
+        {
+            m_is_pressed=false;
+        }
+        virtual const Button& operator=(const AbsInput& b)
+        {
+            AbsInput::operator=(b);
+            m_is_pressed=(dynamic_cast<const Button*>(&b))->m_is_pressed;
+            return *this;
+        }
+        virtual ~Button(){}
+
+        bool is_pressed() const {return m_is_pressed; }
+
+    protected:
+        bool m_is_pressed;
+        virtual void _on_event(const MidiMessage* m){
+            int size=m_actions.size();
+            for(int i=0; i<size; i++){
+                IInputEventListener* l = m_listeners[i];
+                switch(m->type){
+                    case MidiMessage::NOTE_ON:
+                        m_is_pressed=true;
+                        l->on_press(this, *dynamic_cast<const NoteOn*>(m));
+                        break;
+                    case MidiMessage::NOTE_OFF:
+                        m_is_pressed=false;
+                        l->on_release(this, *dynamic_cast<const NoteOff*>(m));
+                        break;
+                }
+            }
+        }
+};
+
+#endif /* SRC_BUTTON_H_ */

+ 71 - 0
include/Command.h

@@ -0,0 +1,71 @@
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include "Socket.h"
+#include <json/json.h>
+#include <iostream>
+#include <vector>
+
+#include "error.h"
+
+class Command
+{
+	public:
+		enum CommandReturn {CONTINUE, EXIT, CLOSE};
+		Command(ISocket* sock);
+		Command();
+		Command(const Command& sock);
+		virtual ~Command(){}
+		virtual const Command& operator=(const Command&);
+
+		virtual Json::Value& operator[](int i){return m_args[i];}
+
+		void print();
+		const std::string& get_name() const {return m_command;}
+		std::vector<Json::Value>& get_args() {return m_args;}
+		static Command parse(ISocket* s);
+		std::string		str() const;
+	protected:
+		std::string m_command;
+		std::vector<Json::Value> m_args;
+		ISocket*    m_io;
+		friend class CommandParser;
+};
+
+class CommandParser
+{
+	public:
+
+		enum Token {
+			PV,
+			IDENT,
+			STRING,
+			INT,
+			FLOAT,
+			BOOL
+		};
+		CommandParser(ISocket* sock) : m_io(sock){}
+		virtual ~CommandParser(){}
+
+
+		static Command parse(ISocket* sock)
+		{
+			CommandParser cp(sock);
+			return cp._parse();
+		}
+
+
+	protected:
+		Command _parse();
+		Token   _next();
+		char    _nc(){ return m_c=m_io->readc();}
+		Token   _set(Token t);
+		ISocket* m_io;
+		Token   m_token;
+		std::string m_current;
+		Json::Value m_val;
+		char     m_c;
+
+};
+
+#endif // COMMAND_H

+ 88 - 0
include/Config.h

@@ -0,0 +1,88 @@
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#include <json/json.h>
+#include "error.h"
+DefineError(JsonPathNotFoundError, -512)
+class Args;
+class Config
+{
+    public:
+        Config(const std::string& file=""){_init(file);}
+        Config(Args& args);
+        virtual ~Config(){}
+
+
+        static void init(Args& args);
+
+		void check();
+        static const std::string  get_string(const std::string& path){
+            Json::Value v = m_instance->_get(path);
+            return v.asString();
+        }
+        static int  get_int(const std::string& path){
+            return m_instance->_get(path).asInt();
+        }
+        static double  get_double(const std::string& path){
+            return m_instance->_get(path).asDouble();
+        }
+        static bool  get_bool(const std::string& path){
+            return m_instance->_get(path).asBool();
+        }
+		static const Json::Value get(const std::string& path){
+            return m_instance->_get(path);
+        }
+
+        static void set(const std::string& path, Json::Value& v){
+            return m_instance->_set(path, v);
+        }
+
+        static void set(const std::string& path, int v){
+            return m_instance->_set(path, Json::Value(v));
+        }
+
+        static void set(const std::string& path, double v){
+            return m_instance->_set(path, Json::Value(v));
+        }
+
+        static void set(const std::string& path, bool v){
+            return m_instance->_set(path, Json::Value(v));
+        }
+
+        static void set(const std::string& path, const std::string& v){
+            return m_instance->_set(path, Json::Value(v));
+        }
+
+        static const std::string APP_DIR(const std::string& x=""){
+            std::string dir = get_string("dirs.app");
+            if(x.size()) return dir+"/"+x;
+            return "./"+x;
+        }
+
+        static const std::string PAD_DIR(const std::string& x=""){
+            std::string dir = get_string("dirs.pads");
+            if(x.size()){
+                if(dir[0]=='/') return dir+"/"+x;
+                return APP_DIR(dir+"/"+x);
+            }
+            if(dir[0]=='/') return dir+"/";
+            return APP_DIR(x);
+        }
+
+
+
+    protected:
+        void                _init(const std::string& ="");
+        Json::Value         _get(const std::string&);
+        void                _set(const std::string&, Json::Value);
+        Json::Value         m_root;
+
+
+
+        static Config*      m_instance;
+        static std::string  m_defaut_config;
+
+};
+
+#endif

+ 48 - 0
include/Controller.h

@@ -0,0 +1,48 @@
+#ifndef CONTROLLER_H
+#define CONTROLLER_H
+
+
+class Controller : public AbsInput
+{
+    public:
+		Controller(PadDefinition* p=NULL) : AbsInput(p, CONTROLLER) {m_value=-1;}
+        Controller(const Controller& b) : AbsInput(b.m_parent, b.type)
+        {
+            m_value=-1;
+        }
+        virtual const Controller& operator=(const AbsInput& b)
+        {
+            AbsInput::operator=(b);
+            m_value=(dynamic_cast<const Controller*>(&b))->m_value;
+            return *this;
+        }
+        virtual ~Controller(){}
+
+        int get_value() const { return m_value; }
+
+    protected:
+        int m_value;
+
+        virtual void _on_event(const MidiMessage* m){
+            int size=m_actions.size();
+
+            for(int i=0; i<size; i++){
+                IInputEventListener* l = m_listeners[i];
+                switch(m->type){
+                    case MidiMessage::CONTROLLER_CHANGE:
+                    {
+                        const ControllerChange* cc =dynamic_cast<const ControllerChange*>(m) ;
+                        m_value=cc->velocity;
+                        l->on_control_change(this, *cc);
+                        break;
+                    }
+                }
+            }
+        }
+};
+
+
+
+
+
+#endif // CONTROLLER_H

+ 29 - 0
include/IInputEventListener.h

@@ -0,0 +1,29 @@
+/*
+ * IInputEventListener.h
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+#ifndef SRC_IINPUTEVENTLISTENER_H_
+#define SRC_IINPUTEVENTLISTENER_H_
+
+class Button;
+class Controller;
+class NoteOn;
+class NoteOff;
+class ControllerChange;
+
+
+class IInputEventListener
+{
+    public:
+        virtual ~IInputEventListener(){}
+        virtual void on_press(Button* b, const NoteOn& m){}
+        virtual void on_release(Button* b, const NoteOff& m){}
+        virtual void on_control_change(Controller* b, const ControllerChange& m){}
+};
+
+
+
+#endif /* SRC_IINPUTEVENTLISTENER_H_ */

+ 202 - 0
include/InputAction.h

@@ -0,0 +1,202 @@
+/*
+ * InputActions.h
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+#ifndef SRC_INPUTACTIONS_H_
+#define SRC_INPUTACTIONS_H_
+
+
+#include "InputAction.h"
+
+#include "MidiMessage.h"
+#include "MidiPort.h"
+
+
+/**
+ * @brief Classe qui représente un message Midi prédéfinie avec des paramètres (channel, key, velocity)
+ * soit défini en dure soit en reprennant les valeur du bouton.
+ */
+class InputAction
+{
+    public:
+        InputAction() : m_name(""), m_message(new NoteOn(1,0,0)), m_current(new NoteOn(1,0,0))
+        {
+            m_key=-1;
+            m_channel=-1;
+            m_velocity=-1;
+        }
+        virtual ~InputAction(){
+            if(m_message) delete m_message;
+            if(m_current) delete m_current;
+        }
+        InputAction(const InputAction& c) : m_name(c.m_name)
+        {
+            this->operator=(c);
+        }
+
+        InputAction(const std::string& name, MidiMessage* m)
+        {
+            m_name=name;
+            m_message=m_current=NULL;
+            m_key=-1;
+            m_channel=-1;
+            m_velocity=-1;
+            set_message(m);
+        }
+
+
+        InputAction(const std::string& name, Json::Value& v)
+        {
+            m_name=name;
+            m_message=m_current=NULL;
+            m_key=-1;
+            m_channel=-1;
+            m_velocity=-1;
+            set_message(MidiMessage::parse(v), false);
+        }
+
+        const InputAction& operator=(const InputAction& c)
+        {
+            m_message=c.m_message->copy();
+            m_current=c.m_current->copy();
+            m_key=c.m_key;
+            m_channel=c.m_channel;
+            m_velocity=c.m_velocity;
+            return *this;
+        }
+
+
+        void set_message(MidiMessage* m, bool copy=true)
+        {
+            if(m_message)
+            {
+                delete m_message;
+                m_message=NULL;
+            }
+            if(m_current)
+            {
+                delete m_current;
+                m_current=NULL;
+            }
+            m_current = m->copy();
+            m_message = copy?m->copy():m;
+            if(m_key>=0) set_key(m_key);
+            if(m_channel>=0) set_channel(m_channel);
+            if(m_velocity>=0) set_velocity(m_velocity);
+        }
+
+        void set_key(int k) {
+            m_key=k;
+            switch(m_message->type){
+                case MidiMessage::NOTE_ON:
+                case MidiMessage::CONTROLLER_CHANGE:
+                case MidiMessage::NOTE_OFF:
+                    (dynamic_cast<Note*>(m_current))->key=k;
+                    break;
+
+            }
+        }
+
+        void set_channel(int k) {
+            m_channel=k;
+            switch(m_message->type){
+                case MidiMessage::NOTE_ON:
+                case MidiMessage::CONTROLLER_CHANGE:
+                case MidiMessage::NOTE_OFF:
+                    (dynamic_cast<Note*>(m_current))->channel=k;
+                    break;
+
+            }
+        }
+
+        void set_velocity(int k) {
+            m_velocity=k;
+            switch(m_message->type){
+                case MidiMessage::NOTE_ON:
+                case MidiMessage::CONTROLLER_CHANGE:
+                case MidiMessage::NOTE_OFF:
+                    (dynamic_cast<Note* >(m_current))->velocity=k;
+                    break;
+
+            }
+
+        }
+
+        MidiMessage* get_message() const {return m_message; }
+        const std::string& get_name() const {return m_name; }
+
+    protected:
+        std::string             m_name;
+        MidiMessage*            m_message;
+        MidiMessage*            m_current;
+
+        int                     m_channel;
+        int                     m_key;
+        int                     m_velocity;
+
+		friend class PadDefinition;
+};
+
+
+class InputActionList : public std::vector<InputAction*>
+{
+    public:
+        InputActionList() : std::vector<InputAction*>(){}
+        InputActionList(const std::string& n, InputAction* i) : std::vector<InputAction*>()
+        {
+            push_back(i);
+            name=n;
+        }
+        InputActionList(const InputActionList& v) : std::vector<InputAction*>(){operator=(v);}
+        InputActionList(const std::string& n, Json::Value& v) : std::vector<InputAction*>(){
+            name=n;
+            add(v);
+        }
+
+        void add(Json::Value& v){
+            int s = v.size();
+            if(s && v[0].isString())
+            {
+                push_back(new InputAction("", v));
+            }else{
+                for(int i=0; i<s; i++)
+                    push_back(new InputAction("", v[i]));
+            }
+        }
+
+        virtual ~InputActionList()
+        {
+            clear();
+        }
+
+        void clear()
+        {
+            int s = size();
+            for(int i=0; i<s; i++) delete at(i);
+            std::vector<InputAction*>::clear();
+        }
+
+        const InputActionList& operator=(const InputActionList& l){
+            std::vector<InputAction*>::operator=(l);
+            name=l.name;
+            return l;
+        }
+
+        MidiMessageList* get_messages()
+        {
+            m_midi.clear();
+            for(int i=0; i<size(); i++)
+                m_midi.push_back(at(i)->get_message());
+            return &m_midi;
+        }
+
+        const std::string& get_name() const {return name;}
+        std::string name;
+        MidiMessageList m_midi;
+};
+
+
+#endif /* SRC_INPUTACTIONS_H_ */

+ 56 - 0
include/InputDefinition.h

@@ -0,0 +1,56 @@
+/*
+ * InputDefintion.h
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+#ifndef SRC_INPUTDEFINTION_H_
+#define SRC_INPUTDEFINTION_H_
+
+#include "utils.h"
+#include "AbsInput.h"
+
+
+/**
+ * @brief Classe qui représente un type d'input défini dans le JSON. Il permet d'instancier et de
+ * configurer les boutons et controllers.
+ */
+class InputDefinition
+{
+    public:
+        InputDefinition(){}
+        InputDefinition(const std::string& name, Json::Value& v){
+            m_name=name;
+
+            Json::Value root = v["actions"];
+
+            for( Json::Value::const_iterator itr = root.begin() ; itr != root.end() ; itr++ ) {
+                std::string k = itr.key().asString();
+                Json::Value v = *itr;
+                m_actions.push_back(new InputActionList(k, new InputAction("", v)));
+            }
+            std::string t = v["type"].asString();
+            if(t=="BUTTON") m_type=BUTTON;
+            if(t=="CONTROLLER") m_type=CONTROLLER;
+
+        }
+        virtual ~InputDefinition(){
+            int s = m_actions.size();
+            for(int i=0; i<s; i++) delete m_actions[i];
+        }
+		AbsInput* instanciate(PadDefinition* p, Json::Value& v);
+
+    protected:
+        std::string m_name;
+        InputType m_type;
+        std::vector<InputActionList*> m_actions;
+
+		friend class PadDefinition;
+
+
+};
+
+
+
+#endif /* SRC_INPUTDEFINTION_H_ */

+ 224 - 0
include/MidiMessage.h

@@ -0,0 +1,224 @@
+#ifndef MIDIMESSAGE_H
+#define MIDIMESSAGE_H
+
+#include "utils.h"
+#include <sstream>
+class MidiException
+{
+	public:
+		MidiException(int c=0, const std::string& m="Success") :
+			code(c), message(m){}
+		MidiException(const std::string& m, const char* t) :
+			code(-1), message(m){}
+        virtual ~MidiException(){}
+        MidiException(const MidiException& m){
+            code=m.code;
+            message=m.message;
+        }
+        int code;
+        std::string message;
+
+        const int SUCCESS=0;
+        const int OVERFLOW_EXCEPTION=1;
+        const int PARSE_EXCEPTION=2;
+
+
+};
+
+class MidiOverflowException : public MidiException
+{
+    public:
+		MidiOverflowException(const std::string& m, const char* t) :
+			MidiException(PARSE_EXCEPTION, "ParseError: "+m){}
+
+        MidiOverflowException() : MidiException(OVERFLOW_EXCEPTION, "Overflow") {}
+        virtual ~MidiOverflowException(){}
+};
+
+class MidiParseException : public MidiException
+{
+    public:
+
+		MidiParseException(const std::string& m, const char* t) :
+			 MidiException(PARSE_EXCEPTION, "ParseError: "+m){}
+        MidiParseException(const std::string& m) : MidiException(PARSE_EXCEPTION, "ParseError: "+m) {}
+        virtual ~MidiParseException(){}
+};
+
+
+
+class MidiMessage
+{
+    public:
+        // types de trames
+        static const uint8_t NOTE_ON = 0x90;
+        static const uint8_t NOTE_OFF = 0x80;
+        static const uint8_t CONTROLLER_CHANGE = 0xB0;
+
+        //constructors / destructors & operators
+        MidiMessage(){}
+        MidiMessage(int _type) : type(_type) {}
+        MidiMessage(const MidiMessage& m ) : type(m.type) {}
+        virtual ~MidiMessage(){}
+
+        // functions
+        virtual int get_bytes(uint8_t* buffer) const =0;
+        virtual std::string to_string() const = 0;
+        bool is_note_on() const { return (type&0xf0)==NOTE_ON; }
+        bool is_note_off() const { return (type&0xf0)==NOTE_OFF; }
+
+        virtual MidiMessage* copy() const
+        {
+            uint8_t tmp[3];
+            int n;
+            n=get_bytes(tmp);
+            return  MidiMessage::parse(tmp, n);
+        }
+
+
+        //data
+        uint8_t type;
+
+        static MidiMessage* parse(const MidiRawMessage&);
+        static MidiMessage* parse(uint8_t * c, int len)
+        {
+            MidiRawMessage msg(c, c+len);
+            return MidiMessage::parse(msg);
+        }
+        static MidiMessage* parse(Json::Value& v);
+};
+
+class MidiMessageList : public std::vector<MidiMessage*>
+{
+    public:
+        MidiMessageList() : std::vector<MidiMessage*>() {}
+        MidiMessageList(const MidiMessageList& mml) : std::vector<MidiMessage*>(mml){
+
+        }
+
+        void clear()
+        {
+            //for(int i=0; i<size(); i++) delete at(i);
+            std::vector<MidiMessage*>::clear();
+        }
+        const MidiMessageList& operator=(const MidiMessageList& m)
+        {
+            int s = m.size();
+            clear();
+
+            for(int i=0; i<s; i++)
+                push_back(m[i]->copy());
+            return m;
+        }
+        MidiMessageList(MidiMessage* mm)  : std::vector<MidiMessage*>()
+        {
+            push_back(mm);
+        }
+        virtual ~MidiMessageList(){
+        }
+
+		void free_content(){
+			int s = size();
+			for(int i=0; i<s; i++)
+				delete at(i);
+			clear();
+		}
+		static MidiMessageList* parse(Json::Value& msg){
+			MidiMessageList* m = new MidiMessageList();
+			int l = msg.size();
+			if(!msg.isArray() || !l){
+				std::cerr << "Erreur array attendu dans pad::input(JSon::Value&)\n";
+				return NULL;
+			}
+			if(msg[0].isString()){
+				m->push_back(MidiMessage::parse(msg));
+			}else{
+				for(int i=0; i<l ;i++)
+				{
+					m->push_back(MidiMessage::parse(msg[i]));
+				}
+			}
+			return m;
+		}
+		static MidiMessageList* parse(const std::string& msg){
+			Json::Value v;
+			std::istringstream iss(msg);
+			iss >> v;
+			return parse(v);
+		}
+};
+
+
+
+class Note : public MidiMessage
+{
+    public:
+        Note() : MidiMessage() {}
+        Note(int _type, int ch, int key, int vel) :
+            MidiMessage(_type), channel(ch), key(key), velocity(vel) {}
+        Note(const Note& n) :  MidiMessage(n.type),
+                channel(n.channel), key(n.key), velocity(n.velocity) {}
+        virtual ~Note(){}
+
+        uint8_t channel;
+        uint8_t key;
+        uint8_t velocity;
+
+
+
+        virtual Note* copy() const
+        {
+            uint8_t tmp[3];
+            int n;
+            n=get_bytes(tmp);
+            Note* m = dynamic_cast<Note*>(MidiMessage::parse(tmp, n));
+            m->channel=channel;
+            m->key=key;
+            m->velocity=velocity;
+            return m;
+        }
+
+
+        virtual int get_bytes(uint8_t* buffer) const {
+            buffer[0]=type|((channel-1)%16);
+            buffer[1]=(key%128);
+            buffer[2]=(velocity%128);
+            return 3;
+        }
+};
+
+
+class NoteOn : public Note {
+    public:
+        NoteOn(int ch, int k, int vel) : Note(MidiMessage::NOTE_ON, ch, k, vel) {}
+        virtual ~NoteOn() {}
+        virtual std::string to_string() const{
+            return "NoteOn("+str(channel)+", "+str(key)+", "+str(velocity)+")";
+        }
+};
+
+
+class NoteOff : public Note {
+    public:
+        NoteOff(int ch, int k, int vel) : Note(MidiMessage::NOTE_OFF, ch, k, vel) {}
+        virtual ~NoteOff() {}
+        virtual std::string to_string() const{
+            return "NoteOn("+str(channel)+", "+str(key)+", "+str(velocity)+")";
+        }
+};
+
+
+
+class ControllerChange : public Note {
+    public:
+        ControllerChange(int ch, int k, int vel) : Note(MidiMessage::CONTROLLER_CHANGE, ch, k, vel) {}
+        virtual ~ControllerChange() {}
+        virtual std::string to_string() const{
+            return "ControllerChange("+str(channel)+", "+str(key)+", "+str(velocity)+")";
+        }
+};
+
+
+
+
+#endif // MIDIMESSAGE_H

+ 89 - 0
include/MidiPort.h

@@ -0,0 +1,89 @@
+#ifndef MIDIPORT_H
+#define MIDIPORT_H
+
+#include "utils.h"
+
+class MidiMessage;
+class MidiMessageList;
+
+typedef void (*MidiInCallback)(double ts, const MidiMessage*, void* data);
+typedef void (*MidiInErrorCallback)(RtMidiError::Type type, const std::string &errorText, void* data);
+
+class IMidiPortListener
+{
+    public:
+        virtual ~IMidiPortListener(){}
+        virtual void on_new_message(double ts, MidiMessage* m)=0;
+};
+
+
+
+class IMidiPortErrorListener
+{
+    public:
+        virtual ~IMidiPortErrorListener(){}
+        virtual void on_error(RtMidiError::Type type, const std::string &errorText)=0;
+};
+
+
+class MidiPortBase
+{
+    public:
+        MidiPortBase();
+        virtual ~MidiPortBase(){}
+        virtual void _error_handler(RtMidiError::Type type, const std::string &errorText);
+
+    protected:
+        void*                       m_error_callback_data;
+        MidiInErrorCallback         m_error_callback;
+        IMidiPortErrorListener*     m_error_listener;
+};
+
+class MidiPortIn : public RtMidiIn, public MidiPortBase
+{
+    public:
+        MidiPortIn(const std::string &clientName);
+        virtual ~MidiPortIn(){}
+
+        void set_callback(MidiInCallback cb, void *cbd=NULL){
+            m_callback=cb;
+            m_listener=NULL;
+            m_callback_data=NULL;
+        }
+
+        void set_listener(IMidiPortListener* cb){
+            m_callback=NULL;
+            m_listener=cb;
+        }
+
+        void _handler(double ts, MidiRawMessage* rm);
+		static std::vector<std::string> get_ports();
+
+
+    protected:
+        void*                       m_callback_data;
+        MidiInCallback              m_callback;
+        IMidiPortListener*          m_listener;
+
+
+
+    private:
+};
+
+
+class MidiPortOut : public RtMidiOut, public MidiPortBase
+{
+    public:
+        MidiPortOut(const std::string &clientName);
+        virtual ~MidiPortOut(){}
+
+
+        void send(const MidiMessage& m );
+        void send(const MidiMessageList& m );
+		static std::vector<std::string> get_ports();
+};
+
+
+
+
+#endif // MIDIPORT_H

+ 119 - 0
include/Operations.h

@@ -0,0 +1,119 @@
+#ifndef PADSELECTIONCONFIGURATION_H
+#define PADSELECTIONCONFIGURATION_H
+
+class MidiMessage;
+
+#include <json/json.h>
+class PadSelection;
+class InputOperation
+{
+    public:
+        enum OperationType {TRANSLATION, DROP, RANGE, REORDER};
+        InputOperation(OperationType t) : m_type(t){}
+        virtual ~InputOperation(){}
+
+        virtual MidiMessage* execute(PadSelection*, MidiMessage* in, int i) const=0;
+
+        OperationType get_type() const {return m_type;}
+
+        static InputOperation* from_json(Json::Value&);
+
+    protected:
+        OperationType       m_type;
+};
+
+class TranslationOperation : public InputOperation
+{
+    public:
+        enum Mode {FIXED, RELATIVE, UNCHANGED};
+        TranslationOperation() : InputOperation(TRANSLATION){
+            m_channel=m_key=m_velocity=0;
+            m_channel_mode=m_key_mode=m_velocity_mode=RELATIVE;
+        }
+        TranslationOperation(Json::Value& v);
+        virtual ~TranslationOperation(){}
+
+        void set(int ch, int k, int vel){
+            m_channel=ch;
+            m_key=k;
+            m_velocity=vel;
+        }
+
+        void set_mode(Mode ch, Mode k, Mode vel){
+            if(ch!=UNCHANGED) m_channel_mode=ch;
+            if(k!=UNCHANGED) m_key_mode=k;
+            if(vel!=UNCHANGED) m_velocity_mode=vel;
+        }
+
+        virtual MidiMessage* execute(PadSelection*, MidiMessage* m, int i) const;
+        static Mode parse_mode(const std::string& v);
+
+    protected:
+        Mode     m_channel_mode;
+        int      m_channel;
+        Mode     m_key_mode;
+        int      m_key;
+        Mode     m_velocity_mode;
+        int      m_velocity;
+
+};
+
+
+class DropOperation : public InputOperation
+{
+    public:
+        enum Mode {FIXED, RELATIVE, UNCHANGED};
+        DropOperation() : InputOperation(DROP){
+        }
+        virtual ~DropOperation(){}
+
+        virtual MidiMessage* execute(PadSelection*, MidiMessage* m, int i) const {return NULL;}
+
+    protected:
+};
+
+class ReorderOperation : public InputOperation
+{
+    public:
+        ReorderOperation() : InputOperation(REORDER){
+            m_offset=0;
+        }
+        ReorderOperation(Json::Value& v);
+        virtual ~ReorderOperation(){}
+
+        virtual MidiMessage* execute(PadSelection*, MidiMessage* m, int i) const ;
+
+    protected:
+        int     m_offset;
+};
+
+
+
+class RangeOperation : public InputOperation
+{
+    public:
+        RangeOperation() : InputOperation(RANGE){
+        }
+        RangeOperation(Json::Value& v) : InputOperation(RANGE)
+        {
+            m_key = v["key"].asInt();
+            m_length = v["length"].asInt();
+            Json::Value a = v["range"];
+            int s = a.size();
+            for(int i=0; i<s; i++) m_range.push_back(a[i].asInt());
+        }
+        virtual ~RangeOperation(){}
+
+        virtual MidiMessage* execute(PadSelection*, MidiMessage* m, int i) const;
+
+    protected:
+        std::vector<int>            m_range;
+        int                         m_key;
+        int                         m_length;
+};
+
+
+
+
+
+#endif // PADSELECTIONCONFIGURATION_H

+ 39 - 0
include/Pad.h

@@ -0,0 +1,39 @@
+#ifndef APPLICATION_H
+#define APPLICATION_H
+
+#include <string>
+#include <json/json.h>
+class PadConfiguration;
+class PadDefinition;
+class MidiMessageList;
+
+class Pad
+{
+    public:
+									Pad();
+									Pad(const std::string& file);
+									Pad(PadConfiguration* p);
+									Pad(PadDefinition* p);
+		virtual                     ~Pad();
+
+        bool                        load_configuration(const std::string& file);
+		void                        remove_configuration();
+		void						connect(bool in = true, bool out=true);
+		const std::string&			get_name() const;
+		bool						is_valid(){return this==NULL;}
+		bool						operator!(){return this==NULL;}
+									operator bool(){return this!=NULL;}
+		void						clear();
+		void						input(MidiMessageList*);
+		void						input(Json::Value&);
+		void						input(const std::string&);
+
+    protected:
+        PadConfiguration*           m_configuration;
+		PadDefinition*              m_pad_definition;
+		std::string					m_name;
+
+
+};
+
+#endif // APPLICATION_H

+ 44 - 0
include/PadConfiguration.h

@@ -0,0 +1,44 @@
+#ifndef PADCONFIGURATION_H
+#define PADCONFIGURATION_H
+
+class PadSelection;
+class MidiMessage;
+class AbsInput;
+class MidiPortOut;
+class MidiPortIn;
+#include <json/json.h>
+#include "MidiPort.h"
+
+
+class PadDefinition;
+class PadConfiguration : public IMidiPortErrorListener, public IMidiPortListener
+{
+    public:
+                                    PadConfiguration(Json::Value& v);
+        virtual                     ~PadConfiguration();
+
+        virtual void                on_error(RtMidiError::Type type, const std::string &errorText);
+        virtual void                on_new_message(double ts, MidiMessage* m);
+        PadSelection*               selection_by_name(const std::string&);
+
+        static PadConfiguration*    from_file(const std::string&);
+        void                        send_init();
+		PadDefinition&              get_pad_definition();
+
+    protected:
+        std::string                 m_name;
+        std::string                 m_description;
+        std::vector<PadSelection*>  m_selections;
+		PadDefinition*              _m_pad_definition_ptr;
+
+		PadDefinition&              m_pad_definition;
+        MidiPortOut*                m_oport;
+        MidiPortOut*                m_cport;
+        MidiPortIn*                 m_iport;
+        int                         m_width;
+        int                         m_height;
+        std::vector<AbsInput*>&     m_matrix;
+};
+
+
+#endif // PADCONFIGURATION_H

+ 118 - 0
include/PadDefinition.h

@@ -0,0 +1,118 @@
+#ifndef PAD_H
+#define PAD_H
+
+
+
+#define PAD_DEFINITION_DIR "pads"
+
+#include "AbsInput.h"
+#include "MidiPort.h"
+
+class InputAction;
+class InputDefinition;
+
+class Button;
+class AbsInput;
+class PadDefinition;
+
+typedef AbsInput* (*CreateInputCallback)(PadDefinition* p, void* data);
+
+
+AbsInput* __pad_default_button(PadDefinition* p, void * data);
+AbsInput* __pad_default_controller(PadDefinition* p, void * data);
+
+class PadDefinition : public IMidiPortListener, public IMidiPortErrorListener
+{
+    public:
+									PadDefinition();
+									PadDefinition(Json::Value v);
+									PadDefinition(const std::string& );
+		virtual                     ~PadDefinition();
+
+        void                        read_conf(const std::string&);
+        void                        read_conf(Json::Value& v);
+
+        virtual MidiPortIn&         get_input_port() {return *m_iport;}
+        virtual MidiPortOut&        get_control_port() {return *m_cport;}
+        virtual MidiPortOut&        get_output_port() { return *m_oport;}
+
+        void                        connect_to_device(bool input=true, bool output=true);
+
+
+        void                        set_error_listener(IMidiPortErrorListener*);
+        void                        set_port_listener(IMidiPortListener*);
+
+        virtual void                on_error(RtMidiError::Type type, const std::string &errorText);
+        virtual void                on_new_message(double ts, MidiMessage* m);
+
+        AbsInput*                   get_by_channel_key(InputType type, int channel, int key);
+        AbsInput*                   input_at(int i, int j=-1);
+        AbsInput*                   get_by_message(const MidiMessage* msg);
+
+
+        InputAction*                get_action_def(const std::string& s);
+        InputDefinition*            get_input_def(const std::string& s);
+        void                        clear();
+        void                        send_all(const std::string&);
+
+
+        Button*                     instanciate_new_button() const;
+        Controller*                 instanciate_new_controller() const;
+
+        std::vector<AbsInput*>&     get_matrix();
+
+        int                         get_width() const {return m_width;}
+        int                         get_height() const {return m_height;}
+        AbsInput*                   at(int x, int y) {return m_matrix[x+y*m_width];}
+		const std::string&			get_name() const {return m_name;}
+
+		static PadDefinition*       from_file(const std::string& str);
+		static PadDefinition*       from_name(const std::string& str);
+
+
+
+    protected:
+        void _dispatch_message(double ts, MidiMessage* m);
+
+
+        //port du Pad Réel, permet de récupérer les entrées
+        MidiPortIn*                         m_iport;
+        bool                                m_iport_open;
+
+        //Port destiné au pad réel afin de le commander
+        MidiPortOut*                        m_cport;
+        bool                                m_cport_open;
+
+        //Port de sortie MIDI "classique" pour y brancher un instrument
+        MidiPortOut*                        m_oport;
+        bool                                m_oport_open;
+
+        //Nom du pad
+        std::string                         m_name;
+
+        //nom du port MIDI du PAD
+        std::string                         m_port_in_name;
+        std::string                         m_port_out_name;
+
+        //taille de la grille
+        int                                 m_width;
+        int                                 m_height;
+
+        //liste des boutons
+        std::vector<AbsInput*>              m_matrix;
+        std::vector<InputDefinition*>       m_input_def;
+
+        IMidiPortErrorListener*            m_error_listener;
+        IMidiPortListener*                 m_port_listener;
+
+        //
+        CreateInputCallback                m_create_button;
+        void*                              m_create_button_data;
+        CreateInputCallback                m_create_controller;
+        void*                              m_create_controller_data;
+
+    private:
+        friend class PadConfiguration;
+};
+
+#endif // PAD_H

+ 58 - 0
include/PadManager.h

@@ -0,0 +1,58 @@
+#ifndef PADMANAGER_H
+#define PADMANAGER_H
+
+#include <json/json.h>
+#include <vector>
+#include <iostream>
+#include <string>
+
+class PadDefinition;
+
+class PadEntry{
+	public:
+		PadEntry(int _id){id=_id; instance=NULL;}
+		virtual ~PadEntry();
+		int				id;
+		std::string		name;
+		std::string		filename;
+		std::string		out_port;
+		std::string		in_port;
+		PadDefinition*	instance;
+};
+
+class PadManager
+{
+    public:
+        PadManager(){_init();}
+		virtual ~PadManager(){_clean();}
+
+        static void init(){
+            if(!m_instance)
+                m_instance=new PadManager();
+        }
+
+
+		static Json::Value						read_pad(const std::string& name) {return m_instance->_read_pad(name);}
+		static bool								has_pad(const std::string& name) {return m_instance->_has_pad(name);}
+		static PadDefinition*					get_pad(const std::string& name) {return m_instance->_get_pad(name);}
+		static PadEntry*						get_info_at(int i) {return m_instance->m_list[i];}
+		static int								count() {return m_instance->m_list.size();}
+		static void								clean(){return m_instance->clean();};
+    protected:
+		void							_clean();
+		PadDefinition*                  _get_pad(const std::string& name);
+		int                             _get_pad_index(const std::string& name);
+        Json::Value                     _read_pad(const std::string& pad_name) const;
+        bool                            _has_pad(const std::string& pad_name) const;
+		PadEntry*                       _add();
+		void                            _init();
+
+		std::vector<PadEntry*> m_list;
+        static PadManager* m_instance;
+		friend class Application;
+};
+
+
+
+
+#endif // PADMANAGER_H

+ 73 - 0
include/PadSelection.h

@@ -0,0 +1,73 @@
+#ifndef PADSELECTION_H
+#define PADSELECTION_H
+
+#include <vector>
+
+#include <json/json.h>
+
+#include "InputAction.h"
+
+class PadConfiguration;
+class AbsInput;
+class MidiMessage;
+class InputOperation;
+class InputInit
+{
+    public:
+        InputInit(int i,int j){x=i; y=j;}
+        virtual ~InputInit();
+        int x;
+        int y;
+        std::vector<InputAction*> actions;
+};
+
+class PadSelection
+{
+    public:
+                                PadSelection(const std::string&, Json::Value&, PadConfiguration*);
+        virtual                 ~PadSelection();
+
+        void                    add(int, int);
+        void                    add(AbsInput*);
+
+        void                    remove(int, int);
+        void                    remove(AbsInput*);
+
+        AbsInput*               at(int,int);
+        bool                    has(int, int) const;
+        bool                    has(AbsInput*) const;
+
+        MidiMessage*            execute(AbsInput*, MidiMessage* m) const;
+        const std::string&      get_name() const {return m_name;}
+        int                     count() const {return m_list.size();}
+        int                     count_x(int x) const;
+        int                     count_y(int y) const;
+        int                     index_at_x(int absindex) const;
+        int                     index_at_y(int absindex) const;
+        AbsInput*               get_child_from_rel_index(int i){return m_list[i];}
+
+        void                    send_init();
+
+
+    protected:
+        void _update_matrix();
+
+        //le pad de la sélection
+        PadConfiguration*               m_pad;
+        std::string                     m_name;
+        int                             m_width;
+        int                             m_height;
+        std::vector<AbsInput*>          m_matrix;
+        std::vector<AbsInput*>          m_list;
+
+        //la configuration
+        std::vector<InputOperation*>    m_operations;
+
+        // les commande d'initialisation pour toute la sélection
+        InputActionList                 m_init;
+
+        friend class PadConfiguration;
+
+};
+
+#endif // PADSELECTION_H

+ 206 - 0
include/Socket.h

@@ -0,0 +1,206 @@
+#ifndef SOCKET_H
+#define SOCKET_H
+
+
+#ifdef WIN32 /* si vous êtes sous Windows */
+
+#include <winsock2.h>
+
+#elif defined (linux) /* si vous êtes sous Linux */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h> /* close */
+#include <netdb.h> /* gethostbyname */
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+#define closesocket(s) close(s)
+typedef int SOCKET;
+typedef struct sockaddr_in SOCKADDR_IN;
+typedef struct sockaddr SOCKADDR;
+typedef struct in_addr IN_ADDR;
+#include <sys/un.h>
+#else /* sinon vous êtes sur une plateforme non supportée */
+
+#error not defined for this platform
+
+#endif
+#include <string>
+#include <istream>
+#include <ostream>
+
+class SocketError
+{
+	public:
+		SocketError(const std::string& s, const std::string& t) : error(s), type(t){}
+		virtual ~SocketError(){}
+		std::string error;
+		std::string type;
+};
+#define DefineSocketError(__Err__) class __Err__ : public SocketError{ public: __Err__(const std::string& e, const std::string& t) : SocketError(e, t){} ~__Err__(){} };
+
+DefineSocketError(ConnectSocketError)
+DefineSocketError(FileSocketNotFoundError)
+DefineSocketError(SendSocketError)
+DefineSocketError(RecieveSocketError)
+DefineSocketError(AcceptSocketError)
+DefineSocketError(BindSocketError)
+DefineSocketError(ListenSocketError)
+DefineSocketError(HostSocketError)
+DefineSocketError(OptSocketError)
+
+#ifndef THROW
+	#define THROW(__ERR__, __MSG__) {std::ostringstream os; os << #__ERR__<< " : " << __MSG__ <<"\n"; std::cerr << os.str(); throw __ERR__(os.str(), #__ERR__);}
+#endif
+
+
+class _AbsSocket
+{
+    public:
+		enum                    SocketType { INET, UNIX, STDIO } ;
+                                _AbsSocket(SocketType);
+        virtual                 ~_AbsSocket();
+        virtual void            close();
+		virtual	bool			is_open();
+
+
+    protected:
+        SocketType              m_type;
+        SOCKET                  m_sock;
+        bool                    m_is_open;
+};
+
+
+class ISocket : public std::istream, public std::ostream
+{
+	public:
+		virtual ~ISocket(){}
+
+		virtual void            write(const void* data, int size)=0;
+		virtual int             read(void* data, int n, bool blocking)=0;
+		virtual ISocket&        putc(int c) {write(&c,1); return *(ISocket*)this;}
+		virtual void            write(const std::string& data){write(data.c_str(), data.size());}
+
+
+		virtual int             read(void* data, int n){return read(data,n,false);};
+		virtual int             readc(){int c; return (read(&c,1)==1)?c:-1;}
+		virtual std::string&    readline(std::string&);
+		virtual	_AbsSocket::SocketType get_type() const =0;
+		virtual void            close()=0;
+		virtual	bool			is_open()=0;
+};
+
+class Socket : public _AbsSocket, public ISocket
+{
+	public:
+		Socket(SocketType t) : _AbsSocket(t){}
+        Socket(SocketType type, SOCKET client) : Socket(type){m_sock=client; m_is_open=true;}
+        virtual ~Socket(){}
+
+		virtual void            write(const void* data, int size);
+		virtual int             read(void* data, int n, bool blocking);
+		virtual	SocketType		get_type() const {return m_type;}
+		virtual void			close(){_AbsSocket::close();};
+		virtual bool			is_open(){return _AbsSocket::is_open();}
+
+};
+
+
+class InetSocket : public Socket
+{
+	public:
+		InetSocket() : Socket(INET){}
+		InetSocket(SOCKET sock) : Socket(INET, sock){}
+        virtual ~InetSocket(){}
+
+		virtual bool    connect(const std::string& addr, int port);
+		virtual Socket::SocketType	get_type() const {return Socket::INET;}
+
+    protected:
+        std::string     m_hostname;
+        int             m_port;
+};
+
+class UnixSocket : public Socket
+{
+    public:
+        UnixSocket() : Socket(UNIX){}
+        virtual ~UnixSocket(){}
+
+		virtual bool    connect(const std::string& addr);
+		virtual Socket::SocketType	get_type() const {return Socket::UNIX;}
+
+    protected:
+        std::string     m_hostname;
+        int             m_port;
+};
+
+
+class IAbsServer
+{
+	public:
+		virtual						~IAbsServer(){}
+		virtual ISocket*			accept()=0;
+		virtual Socket::SocketType	get_type() const=0;
+
+};
+
+class InetServer : public _AbsSocket, public IAbsServer
+{
+	public:
+						InetServer();
+        virtual         ~InetServer(){}
+
+        virtual void    listen(int port);
+		virtual ISocket* accept();
+		virtual Socket::SocketType	get_type() const {return Socket::INET;}
+
+
+};
+
+class UnixServer : public _AbsSocket, public IAbsServer
+{
+    public:
+        UnixServer();
+        virtual         ~UnixServer();
+
+        virtual void    listen(const std::string& path){return listen(path, true);}
+        virtual void    listen(const std::string& path, bool);
+		virtual ISocket* accept();
+		virtual Socket::SocketType	get_type() const {return Socket::UNIX;}
+
+    protected:
+        std::string     m_path;
+};
+
+
+class StdioSocket :  public ISocket
+{
+	public:
+									StdioSocket(int first){m_first=first;}
+		virtual						~StdioSocket(){}
+
+		virtual void				write(const void* data, int size);
+		virtual int					read(void* data, int n, bool blocking);
+		virtual	Socket::SocketType	get_type() const {return Socket::STDIO;}
+		virtual void				close(){};
+		virtual bool				is_open(){return false;}
+
+	public:
+		int     m_first;
+
+};
+
+
+class StdioServer : public IAbsServer
+{
+	public:
+									StdioServer() {}
+		virtual						~StdioServer(){}
+		virtual ISocket*			accept();
+		virtual Socket::SocketType	get_type() const {return Socket::STDIO;}
+};
+
+#endif // SOCKET_H

+ 30 - 0
include/error.h

@@ -0,0 +1,30 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+#include <string>
+class Error
+{
+	public:
+		Error(const std::string& s, const char* t, int c=-255) : error(s), type(t), code(c){}
+		virtual ~Error(){}
+		std::string error;
+		std::string type;
+		int			code;
+};
+
+#define DefineError(__Err__, __CODE__) class __Err__ : public Error{ public: __Err__(const std::string& e, const std::string& t) : Error(e, #__Err__, __CODE__){} ~__Err__(){} };
+
+DefineError(ArgumentCommandError, -1)
+DefineError(NoPadSelectedError, -2)
+DefineError(ParseError, -3)
+DefineError(LexerError, -4)
+DefineError(NotFoundError, -5)
+DefineError(MalformedError, -6)
+DefineError(MidiError, -7)
+DefineError(UnknownError, -8)
+DefineError(ResponseOK, 0)
+
+#define THROW(__ERR__, __MSG__) {std::ostringstream os; os << #__ERR__<< " : " << __MSG__ <<"\n"; std::cerr << os.str(); throw __ERR__(os.str(), #__ERR__);}
+
+
+#endif // ERROR_H

+ 55 - 0
include/utils.h

@@ -0,0 +1,55 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <vector>
+#include <iostream>
+#include "RtMidi.h"
+#include <json/json.h>
+#include "error.h"
+
+typedef std::vector<unsigned char> MidiRawMessage;
+
+std::string str(int x, const char* format="%d");
+std::string str(const MidiRawMessage& x);
+bool start_with(const std::string& s1, const std::string& s2, bool ignoreCase=false);
+
+Json::Value json_parse_file(const std::string& filename);
+
+
+
+class Fs {
+    public:
+        static bool is_file(const std::string&);
+        static bool is_dir(const std::string&);
+        static bool is_file_exists(const std::string&, int);
+        static bool exists(const std::string&);
+        static bool remove(const std::string&);
+};
+
+
+
+class Args
+{
+    public:
+        Args(int ac, char** argv);
+        virtual ~Args(){}
+
+        std::string config_file;
+        std::string pad_file;
+        std::string override_app_dir;
+        std::string override_pad_dir;
+        bool        continue_app;
+        bool        use_stdin;
+        bool        use_pipe;
+        std::string use_pipe_filename;
+        bool        use_socket;
+        int         use_socket_port;
+		bool		keep_alive;
+
+
+
+};
+
+
+
+#endif // UTILS_H

+ 95 - 0
main.cpp

@@ -0,0 +1,95 @@
+
+#include <cstdio>
+#include <RtMidi.h>
+#include "MidiMessage.h"
+#include "MidiPort.h"
+#include "PadDefinition.h"
+#include <unistd.h>
+#include "Config.h"
+#include "PadManager.h"
+#include "simplemidi.h"
+#include "Pad.h"
+#include "Application.h"
+
+class TMP : public IMidiPortListener
+{
+    public:
+        virtual ~TMP(){}
+        virtual void on_new_message(double ts, const MidiMessage* m){
+            std::cout << m->to_string() << "\n";
+        }
+};
+
+#include "Socket.h"
+#include "Command.h"
+int main(int argc, char** argv)
+{
+    const char* fake_argv[] = {
+		".",
+		"--command", "inet", "8081", "/home/fanch/Programmation/simplemidi/utils/conf3.conf"
+    };
+	if(argc==1){
+		argv=(char**)fake_argv;
+		argc=sizeof(fake_argv)/sizeof(*fake_argv);
+	}
+	if(0){
+		for(int i=0;i<argc; i++)
+			std::cout << "'" << argv[i] <<"'\n";
+		std::cout << "\n";
+	}
+
+	Args args(argc, argv);
+	Config::init(args);
+	PadManager::init();
+	Application app;
+	app.process();
+
+
+
+
+
+/*
+	StdioServer serv;
+	if(serv.get_type()==Socket::STDIO) std::cerr << "> ";
+	bool do_exit=false;
+	while(!do_exit){
+		ISocket* sock = serv.accept();
+		Command c = Command::parse(sock);
+		c.print();
+		sock->write("\n> ");
+		if(c.get_name()=="exit") do_exit=true;
+	}
+*/
+
+/*
+    argv=(char**)fake_argv;
+    argc=sizeof(fake_argv)/sizeof(*fake_argv);
+
+    Args args(argc, argv);
+    Config::init(args);
+    PadManager::init();
+    Application app;
+    if(!args.continue_app)  return 0;
+    while(1)
+    {
+        //app.load_configuration("/home/fanch/Programmation/simplemidi/utils/conf.conf");
+
+        app.load_configuration("/home/fanch/Programmation/simplemidi/utils/conf2.conf");
+        usleep(5000000);
+        app.load_configuration("/home/fanch/Programmation/simplemidi/utils/conf3.conf");
+        usleep(5000000);
+	}*/
+
+    /*
+    std::cerr << "Start\n";
+    std::string file="utils/.simple_midi_exchange";
+//    /std::string args="utils/conf2.conf";
+    if(argc>1) file=argv[1];
+    std::cerr << "File = " << file <<"\n";
+    PadConfguration *p = PadConfguration::from_file(file);
+    p->connect_to_device();
+    p->send_init();
+    std::cout << "LOOPING\n";
+    while(1);*/
+
+}

+ 759 - 0
pads/apc.json

@@ -0,0 +1,759 @@
+{
+    "name": "APC Mini",
+    "width": 10,
+    "height": 12,
+    "ports": {
+        "input": "APC MINI:APC MINI MIDI",
+        "output": "APC MINI:APC MINI MIDI"
+    },
+    "inputs": {
+        "button": {
+            "type": "BUTTON",
+            "actions": {
+                "on": [
+                    "noteon",
+                    -1,
+                    -1,
+                    1
+                ],
+                "off": [
+                    "noteon",
+                    -1,
+                    -1,
+                    0
+                ],
+                "clear": [
+                    "noteon",
+                    -1,
+                    -1,
+                    0
+                ],
+                "green": [
+                    "noteon",
+                    -1,
+                    -1,
+                    1
+                ],
+                "_blink": [
+                    "noteon",
+                    -1,
+                    -1,
+                    2
+                ],
+                "red": [
+                    "noteon",
+                    -1,
+                    -1,
+                    3
+                ],
+                "red_blink": [
+                    "noteon",
+                    -1,
+                    -1,
+                    4
+                ],
+                "yellow": [
+                    "noteon",
+                    -1,
+                    -1,
+                    5
+                ],
+                "yellow_blink": [
+                    "noteon",
+                    -1,
+                    -1,
+                    6
+                ]
+            }
+        },
+        "circlebutton": {
+            "type": "BUTTON",
+            "actions": {
+                "on": [
+                    "noteon",
+                    -1,
+                    -1,
+                    1
+                ],
+                "off": [
+                    "noteon",
+                    -1,
+                    -1,
+                    0
+                ],
+                "clear": [
+                    "noteon",
+                    -1,
+                    -1,
+                    0
+                ]
+            }
+        },
+        "controller": {
+            "type": "CONTROLLER",
+            "actions": {}
+        }
+    },
+    "matrix": [
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 56,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 57,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 58,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 59,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 60,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 61,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 62,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 63,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 82,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 48,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 49,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 50,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 51,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 52,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 53,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 54,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 55,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 83,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 40,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 41,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 42,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 43,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 44,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 45,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 46,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 47,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 84,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 32,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 33,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 34,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 35,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 36,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 37,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 38,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 39,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 85,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 24,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 25,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 26,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 27,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 28,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 29,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 30,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 31,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 86,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 16,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 17,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 18,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 19,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 20,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 21,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 22,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 23,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 87,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 8,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 9,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 10,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 11,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 12,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 13,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 14,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 15,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 88,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 0,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 1,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 2,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 3,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 4,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 5,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 6,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 7,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 89,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 64,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 65,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 66,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 67,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 68,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 69,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 70,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 71,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        {
+            "type": "button",
+            "channel": 1,
+            "key": 98,
+            "velocity": 0,
+            "locked": null
+        },
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        {
+            "type": "controller",
+            "channel": 1,
+            "key": 48,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "controller",
+            "channel": 1,
+            "key": 49,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "controller",
+            "channel": 1,
+            "key": 50,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "controller",
+            "channel": 1,
+            "key": 51,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "controller",
+            "channel": 1,
+            "key": 52,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "controller",
+            "channel": 1,
+            "key": 53,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "controller",
+            "channel": 1,
+            "key": 54,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "controller",
+            "channel": 1,
+            "key": 55,
+            "velocity": 0,
+            "locked": null
+        },
+        {
+            "type": "controller",
+            "channel": 1,
+            "key": 56,
+            "velocity": 0,
+            "locked": null
+        },
+        null
+    ]
+}

+ 9 - 0
simplemidi.h

@@ -0,0 +1,9 @@
+#ifndef SIMPLEMIDI_H
+#define SIMPLEMIDI_H
+
+#include "Button.h"
+#include "Controller.h"
+#include "PadDefinition.h"
+#include "PadConfiguration.h"
+
+#endif // SIMPLEMIDI_H

+ 12 - 0
src/AbsInput.cpp

@@ -0,0 +1,12 @@
+/*
+ * Input.cpp
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+#include "AbsInput.h"
+#include "PadDefinition.h"
+MidiPortIn& AbsInput::get_input_port() {return m_parent->get_input_port();}
+MidiPortOut& AbsInput::get_control_port() {return m_parent->get_control_port();}
+MidiPortOut& AbsInput::get_output_port() { return m_parent->get_output_port();}

+ 330 - 0
src/Application.cpp

@@ -0,0 +1,330 @@
+#include "Application.h"
+#include "Pad.h"
+#include <iostream>
+#include "Config.h"
+#include "Socket.h"
+#include "Command.h"
+#include "utils.h"
+#include "PadManager.h"
+#include "MidiPort.h"
+#include <sstream>
+#include <fstream>
+
+
+
+void CommandLog::write(ISocket& sock, const std::string& append)
+{
+
+	sock.write(::str(error)+" "+message+"\n");
+	std::string out=" ", _str=str()+append;
+	int len = _str.size();
+	for(int i=0; i<len; i++){
+		out+=_str[i];
+		if(_str[i]=='\n') out+=" ";
+	}
+	sock.write(out);
+}
+
+void add_commands(Application& app);
+Application::Application()
+{
+	m_pad_last_id=-1;
+	m_server=NULL;
+	int port = Config::get_int("server.port");
+	const std::string& type = Config::get_string("server.type");
+	const std::string& addr = Config::get_string("server.address");
+
+	if(type=="af_unix")
+	{
+		UnixServer* p = new UnixServer();
+		m_server=p;
+		p->listen(addr);
+	}
+	else if(type=="inet")
+	{
+		InetServer* p = new InetServer();
+		m_server=p;
+		p->listen(port);
+	}
+	else
+	{
+		StdioServer* p = new StdioServer();
+		m_server=p;
+	}
+	add_commands(*(Application*)this);
+	_auto_load_pad();
+}
+
+Application::~Application()
+{
+	delete m_server;
+	int i, s=m_callbacks.size();
+	for(i=0; i<s; i++)
+		delete m_callbacks[i];
+	m_callbacks.clear();
+}
+
+void Application::process_next_command(int n)
+{
+	if(m_server->get_type()==Socket::STDIO) std::cerr << "> ";
+	Command::CommandReturn do_continue = Command::CONTINUE;
+	ISocket* sock = NULL;
+	while(do_continue!=Command::EXIT){
+		if(!sock || !sock->is_open() || do_continue==Command::CLOSE || !Config::get_bool("server.keep-alive")){
+			if(sock) delete sock;
+			sock=m_server->accept();
+		}
+
+		if(m_server->get_type()!=Socket::STDIO)
+			sock->write("\n> ");
+		Command c;
+		try{
+			c=Command::parse(sock);
+		} catch (ParseError p) {
+			SEND(*sock, p.error);
+		}
+
+		if(c.get_name()!="")do_continue=_call(c, *sock);
+
+		// fermeture de la socket si keep alive n'est pas activé et si ce n'est pas stdio
+		if(m_server->get_type()!=Socket::STDIO
+		   && !Config::get_bool("server.keep-alive")){
+			sock->close();
+		}
+
+		// Affichage du prompt ("> ") si la socket est stdio (pour les autres, c'est fait en début de boucle)
+		if(!do_continue && m_server->get_type()==Socket::STDIO) sock->write("> ");
+		if(n<0) n--;
+
+	}
+}
+
+void Application::process()
+{
+	process_next_command(-1);
+}
+
+Pad& Application::operator[](int id)
+{
+	if(id>=0 && id<m_pads.size())
+		return *m_pads[id].second;
+	return *(Pad*)NULL;
+}
+
+Pad& Application::operator[](const std::string& name)
+{
+	return get_pad(name);
+}
+
+Pad& Application::set_pad(int x)
+{
+	int i, s=m_pads.size();
+	for(i=0; i<s; i++)
+	{
+		if(m_pads[i].first==x)
+			break;
+	}
+	if(i==s){
+		THROW(NotFoundError, "Le pad id(" << x << ") est introuvable");
+	}else{
+		m_current_index=x;
+		m_current=&get_pad(x);
+	}
+	return *m_current;
+}
+
+Pad& Application::set_pad(const std::string& name)
+{
+	int i, s=m_pads.size();
+	for(i=0; i<s; i++)
+	{
+		if(m_pads[i].second->get_name()==name)
+			break;
+	}
+	if(i==s){
+		std::cerr << "Le pad '" << name << "' est introuvable\n";
+	}else{
+		m_current=&get_pad(name);
+		m_current_index=i;
+	}
+	return *m_current;
+}
+
+Pad& Application::get_pad(const std::string& name)
+{
+	if(name=="") return *m_current;
+	int i, s=m_pads.size();
+	for(i=0; i<s; i++)
+		if(m_pads[i].second->get_name()==name)
+			return *m_pads[i].second;
+	std::cerr << "Pad '"<< name << " ' introuvable\n";
+	return *(Pad*)NULL;
+}
+
+Pad& Application::get_pad(int id)
+{
+	if(id<0) return *m_current;
+	int i, s=m_pads.size();
+	for(i=0; i<s; i++)
+		if(m_pads[i].first==id)
+			return *m_pads[i].second;
+	std::cerr << "Pad id("<< id << " ) introuvable\n";
+	THROW(NotFoundError, "Pad not found");
+}
+
+int Application::add_pad(Pad* p)
+{
+	std::pair<int, Pad*> pp(++m_pad_last_id, p);
+	m_pads.push_back(pp);
+	return m_pads[m_pads.size()-1].first;
+}
+
+int Application::add_pad(const std::string& name)
+{
+	Pad* pad = new Pad(PadManager::get_pad(name));
+	std::pair<int, Pad*> pp(++m_pad_last_id, pad);
+	m_pads.push_back(pp);
+	return m_pads[m_pads.size()-1].first;
+}
+
+bool Application::has_pad(const std::string& name)
+{
+	int i, s=m_pads.size();
+	for(i=0; i<s; i++)
+		if(m_pads[i].second->get_name()==name)
+			return true;
+	return false;
+}
+
+void Application::remove_pad(int id)
+{
+	int i, s=m_pads.size()-1;
+	for(i=s; i>=0; i--)
+		if(m_pads[i].first==id){
+			m_pads.erase(m_pads.begin()+i);
+			return;
+		}
+}
+
+void Application::remove_pad(const std::string& name)
+{
+	int i, s=m_pads.size()-1;
+	for(i=s; i>=0; i--)
+		if(m_pads[i].second->get_name()==name)
+			m_pads.erase(m_pads.begin()+i);
+}
+
+void Application::add_command(const std::string& name, CommandCallback c, const std::string& h)
+{
+	m_callbacks.push_back(new CommandHandler(name, c, h));
+}
+
+Command::CommandReturn Application::_call(Command& c, ISocket& sock)
+{
+	int i, s=m_callbacks.size();
+	for(i=0; i<s; i++)
+	{
+		if(c.get_name()==m_callbacks[i]->name)
+		{
+			CommandLog log;
+			try {
+				Command::CommandReturn ret = m_callbacks[i]->command(*(Application*)this, *m_current, c.get_args(), log);
+				log.write(sock);
+				return ret;
+			} catch(Error err){
+				log.set(err.code, err.type);
+				log.write(sock, err.error);
+			} catch(SocketError err){
+				log.set(-1024, err.type);
+				log.write(sock, err.error);
+			}
+			return Command::CONTINUE;
+		}
+	}
+	CommandLog log;
+	log.set(1, "Commande introuvable");
+	log << "Command '"<< c.get_name() << "' introuvable";
+	log.write(sock);
+	return Command::CONTINUE;
+}
+
+int Application::get_id(const std::string& name){
+	int i, s=m_pads.size();
+	for(i=0; i<s; i++)
+		if(name==m_pads[i].second->get_name())
+			return m_pads[i].first;
+	return -1;
+}
+
+void Application::help(CommandLog& out)
+{
+	int i, s=m_callbacks.size();
+	for(i=0; i<s; i++)
+	{
+		out << m_callbacks[i]->name+" : " << m_callbacks[i]->help+"\n";
+	}
+}
+
+
+bool Application::load_pad_configuration(const std::string& p)
+{
+	if(Fs::is_file(p)){
+		std::ifstream ifs(p);
+		std::string padname;
+		Json::Value c;
+		ifs >> c;
+		if(c.isObject() && c.isMember("pad") && c["pad"].isString())
+			padname=c["pad"].asString();
+
+		Pad& pad = get_pad(padname);
+		if(pad){
+			pad.load_configuration(p);
+		}else{
+			int x = add_pad(padname);
+			Pad* pad = &get_pad(x);
+			if(pad){
+				pad->load_configuration(p);
+			}else{
+				THROW(UnknownError, "Impossible d'ajouter le pad '"<<padname<<"'");
+			}
+		}
+	}
+	return true;
+}
+
+bool Application::load_pad_definition(const std::string& padname)
+{
+	Pad& pad = get_pad(padname);
+	if(!pad){
+		int x = add_pad(padname);
+		Pad* pad = &get_pad(x);
+		if(!pad){
+			THROW(UnknownError, "Impossible d'ajouter le pad '"<<padname<<"'");
+		}
+	}
+	return true;
+}
+
+
+void Application::_auto_load_pad()
+{
+
+	std::vector<std::string> ports = MidiPortIn::get_ports();
+	int n = PadManager::count(), nports= ports.size();
+
+	for(int i=0; i<n; i++){
+		PadEntry* p = PadManager::get_info_at(i);
+		if(!has_pad(p->name)){
+			for(int j=0; j<nports; j++){
+				if(start_with(ports[j], p->in_port)){
+					int x = add_pad(p->name);
+					Pad& p = get_pad(x);
+					p.connect(true, true);
+
+				}
+			}
+		}
+
+	}
+}
+

+ 9 - 0
src/Button.cpp

@@ -0,0 +1,9 @@
+/*
+ * Button.cpp
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+#include "Button.h"
+

+ 137 - 0
src/Command.cpp

@@ -0,0 +1,137 @@
+#include "Command.h"
+
+#include "error.h"
+Command::Command(ISocket* sock) : m_io(sock)
+{
+
+}
+
+Command::Command()
+{
+
+}
+
+Command::Command(const Command& sock) : m_io(sock.m_io)
+{
+
+}
+
+const Command& Command::operator=(const Command& m)
+{
+	m_command=m.m_command;
+	m_args=m.m_args;
+	m_io=m_io;
+	return m;
+}
+
+Command Command::parse(ISocket* s){
+	return CommandParser::parse(s);
+}
+
+CommandParser::Token CommandParser::_set(Token t)
+{
+	m_token=t;
+	switch (t) {
+		case PV: m_current=";"; break;
+		case INT: m_val=Json::Value(atoi(m_current.c_str())); break;
+		case FLOAT: m_val=Json::Value(atof(m_current.c_str())); break;
+		case STRING: m_val=Json::Value(m_current); break;
+		case IDENT:
+			if(m_current=="true"){
+				m_val=Json::Value(true);
+				m_token=BOOL;
+			}
+			else if(m_current=="false"){
+				m_val=Json::Value(false);
+				m_token=BOOL;
+			}
+			else{
+				m_val=Json::Value(m_current);
+			}
+			break;
+	}
+	return t;
+}
+
+CommandParser::Token CommandParser::_next()
+{
+
+	m_current="";
+	while(m_c==' ' || m_c=='\t' || m_c=='\r')_nc();
+	if(m_c==';' || m_c=='\n'){
+		return _set(PV);
+	}
+	else if(m_c=='"'){
+		while(_nc()!='"')
+			m_current+=m_c;
+		_nc();
+		_set(STRING);
+	}
+	else if(m_c=='\''){
+		while(_nc()!='\'')
+			m_current+=m_c;
+		_nc();
+		_set(STRING);
+	}
+	else if(m_c>='0' && m_c<='9')
+	{
+		m_current+=m_c;
+		int dot=0;
+		while(dot<=1 && ((_nc()>='0' && m_c<='9')||m_c=='.') )
+			m_current+=m_c;
+		if(dot>1){
+			THROW(LexerError, "Un seul point ('.') est autorisé pour faire un nombre");
+		}
+		 _set(dot?FLOAT:INT);
+	}
+	else if( (m_c>='A' && m_c<='Z') ||(m_c>='a' && m_c<='z') || m_c=='_'){
+
+		while((m_c>='A' && m_c<='Z') ||(m_c>='a' && m_c<='z') || m_c=='_'){
+			m_current+=m_c;
+			_nc();
+		}
+		_set(IDENT);
+	}
+	//while(m_c==' ' || m_c=='\t' || m_c=='\r') _nc();
+	return m_token;
+
+}
+
+
+Command CommandParser::_parse()
+{
+
+	Command c(m_io);
+	_nc();
+	if(_next()!=PV){
+		if(m_token!=IDENT){
+			THROW(ParseError,"Erreur ident attendu en début de commande\n");
+		}
+
+		c.m_command=m_current;
+
+		while(_next()!=PV){
+			c.m_args.push_back(m_val);
+		}
+	}
+	return c;
+}
+
+
+
+std::string Command::str() const{
+	int s = m_args.size();
+	std::string out;
+	out+=m_command+"(";
+
+	for(int i=0; i<s; i++){
+		if(i) out+=", ";
+		out+=m_args[i].asString();
+	}
+	out+=")";
+}
+
+void Command::print()
+{
+	m_io->write(str());
+}

+ 203 - 0
src/Config.cpp

@@ -0,0 +1,203 @@
+#include "Config.h"
+
+/*
+
+  {
+    "dirs" : {
+        "app" : ".",
+        "pads" : "pads"
+    },
+    "autoconnect" : true,
+	"clear" : true,
+	"server" : {
+		"type" : "stdin" // "stdin", "af_unix", "af_inet"
+		["address" : PATH or DOMAIN_NAME,]
+		["port" : PORT,]
+		["keep-alive" : false]
+	},
+
+  }
+
+
+  */
+std::string Config::m_defaut_config="{\
+                                    \"dirs\" : {\
+                                        \"app\" : \".\",\
+                                        \"pads\" : \"pads\"\
+                                    },\
+                                    \"autoconnect\" : true,\
+									\"clear\" : true,\
+									\"server\" : {\
+										\"type\" : \"stdin\",\
+										\"keep-alive\" :false,\
+									},\
+                                  }";
+
+Config* Config::m_instance=NULL;
+
+#include <fstream>
+#include <sstream>
+#include <iostream>
+#include "utils.h"
+Config::Config(Args& args)
+{
+    _init(args.config_file);
+    if(args.override_app_dir!="") _set("dirs.app", Json::Value(args.override_app_dir));
+
+    if(args.override_pad_dir!=""){
+        Json::Value v(args.override_pad_dir);
+        _set("dirs.pads", v);
+    }
+
+	if(args.use_pipe){
+		_set("server.type", "af_unix");
+		_set("server.address", args.use_pipe_filename);
+		_set("server.port", -1);
+	}else if(args.use_socket){
+		_set("server.type", "inet");
+		_set("server.address", "localhost");
+		_set("server.port", args.use_socket_port);
+	}else if(args.use_stdin){
+		_set("server.type", "stdin");
+		_set("server.address", "");
+		_set("server.port", -1);
+	}
+
+	if(args.keep_alive){
+		_set("server.keep-alive", args.keep_alive==1);
+	}
+
+	if(0)
+	{
+		std::cerr << "Affichage de la config"  << "\n";
+		std::cerr << m_root << "\n";
+	}
+}
+
+void Config::check() {
+	if(!Fs::is_dir(Config::APP_DIR())){
+		std::cerr << "Le dossier de l'application '"<< Config::APP_DIR() <<"' est inaccessible\nFermeture...\n";
+		exit(-1);
+	}
+	if(!Fs::is_dir(Config::PAD_DIR())){
+		std::cerr << "Le dossier de pad '"<< Config::PAD_DIR() <<"' est inaccessible\nFermeture...\n";
+		exit(-1);
+	}
+}
+
+void Config::init(Args &args){
+    m_instance=new Config(args);
+	m_instance->check();
+}
+
+void Config::_init(const std::string& file)
+{
+    Json::Value v;
+    if(file=="")
+    {
+        std::istringstream s(m_defaut_config);
+        std::cerr << "Lecture de la config par défaut\n";
+        s >> v;
+    }
+    else{
+        std::ifstream fd(file);
+        if(fd.is_open())
+        {
+            try{
+                fd >> v;
+            }catch(...){
+                std::cerr << "Erreur: problème dans le fichier de config '"<< file <<"'\n";
+                fd.close();
+                return _init();
+            }
+
+            fd.close();
+        }
+        else
+        {
+            std::cerr << "Impossible de lire le fichier de config '"<<file<<"'\n";
+            return _init();
+        }
+    }
+    m_root=v;
+
+
+}
+
+Json::Value Config::_get(const std::string& _path)
+{
+    const char* path = _path.c_str();
+    char* curr = (char*) path;
+
+    Json::Value v=m_root;
+    while(*curr){
+        std::string tmp;
+        while(*curr && *curr!='.') tmp+=(*curr++);
+        if(!v.isObject() || !v.isMember(tmp)){
+			THROW(JsonPathNotFoundError, "L'option '"<<_path<<"' est inconnu ('"<<tmp<<"' est introuvable)");
+        }
+        v=v[tmp];
+        if(*curr) curr++;
+    }
+    return v;
+}
+
+
+void Config::_set(const std::string& _path, Json::Value val)
+{
+    const char* path = _path.c_str();
+    char* curr = (char*) path;
+
+    Json::Value* v=&m_root;
+    while(*curr){
+        std::string tmp;
+        while(*curr && *curr!='.') tmp+=(*curr++);
+		if(!v->isObject()){
+			(*v)=Json::ValueType::objectValue;
+        }
+		if(!v->isMember(tmp)){
+			(*v)[tmp]=Json::ValueType::objectValue;
+		}
+        v=&((*v)[tmp]);
+        if(*curr) curr++;
+    }
+    *v=val;
+}
+
+/*
+ * cmake_minimum_required(VERSION 3.5)
+
+project(simplemidi LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(src main.cpp
+	src/MidiPort.cpp
+	src/AbsInput.cpp
+	src/utils.cpp
+	src/Application.cpp
+	src/InputDefinition.cpp
+	src/IInputEventListener.cpp
+	src/Pad.cpp
+	src/Button.cpp
+	src/commands.cpp
+	src/InputAction.cpp
+	src/MidiMessage.cpp
+	src/PadSelection.cpp
+	src/Operations.cpp
+	src/PadConfiguration.cpp
+	src/Command.cpp
+	src/Socket.cpp
+	src/PadManager.cpp
+	src/Config.cpp
+	src/PadDefinition.cpp)
+
+
+add_executable(simplemidi ${src})
+include_directories(include)
+include_directories(/usr/include/rtmidi)
+target_link_libraries(simplemidi /usr/lib/libjsoncpp.so)
+target_link_libraries(simplemidi /usr/lib/librtmidi.so)
+
+*/

+ 9 - 0
src/IInputEventListener.cpp

@@ -0,0 +1,9 @@
+/*
+ * IInputEventListener.cpp
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+#include "IInputEventListener.h"
+

+ 8 - 0
src/InputAction.cpp

@@ -0,0 +1,8 @@
+/*
+ * InputActions.cpp
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+

+ 40 - 0
src/InputDefinition.cpp

@@ -0,0 +1,40 @@
+/*
+ * InputDefintion.cpp
+ *
+ *  Created on: 5 déc. 2020
+ *      Author: fanch
+ */
+
+#include "InputDefinition.h"
+
+#include "Button.h"
+#include "Controller.h"
+#include "PadDefinition.h"
+AbsInput* InputDefinition::instanciate(PadDefinition* p, Json::Value& v)
+{
+    if(m_type==BUTTON)
+    {
+        Button* b = p->instanciate_new_button();
+        b->x=b->y=0;
+        b->realchannel = (!v.isMember("channel") || v["channel"].isNull())?-1:v["channel"].asInt();
+        b->realkey = (!v.isMember("key") || v["key"].isNull())?-1:v["key"].asInt();
+        b->realvel = 0;
+        b->locked = "";
+        b->m_actions = m_actions;
+
+        return b;
+    }
+    else if(m_type==CONTROLLER)
+    {
+        Controller* b = p->instanciate_new_controller();
+        b->x=b->y=0;
+        b->realchannel = (!v.isMember("channel") || v["channel"].isNull())?-1:v["channel"].asInt();
+        b->realkey = (!v.isMember("key") || v["key"].isNull())?-1:v["key"].asInt();
+        b->realvel = 0;
+        b->locked = "";
+        b->m_actions = m_actions;
+        return b;
+    }
+    return NULL;
+}
+

+ 52 - 0
src/MidiMessage.cpp

@@ -0,0 +1,52 @@
+#include "MidiMessage.h"
+#include "error.h"
+
+MidiMessage* MidiMessage::parse(const MidiRawMessage& m)
+{
+    uint8_t type = m[0];
+
+    if(type<0xF0)
+    {
+        uint8_t ch = (type&0x0f)+1;
+        type&=0xF0;
+        switch(type){
+            case MidiMessage::NOTE_ON:
+                if(m.size()!=3)
+					THROW(MidiParseException, "NoteOn need 3 bytes (found:"+str(m.size())+" in "+str(m)+")");
+                return new NoteOn(ch, m[1], m[2]);
+            case MidiMessage::NOTE_OFF:
+                if(m.size()!=3)
+					THROW(MidiParseException, "NoteOff need 3 bytes (found:"+str(m.size())+" in "+str(m)+")");
+                return new NoteOff(ch, m[1], m[2]);
+            case MidiMessage::CONTROLLER_CHANGE:
+                return new ControllerChange(ch, m[1], m[2]);
+
+            default:
+                printf("%2X %2X %2X\n", m[0], m[1], m[2]);
+				THROW(MidiParseException, "Unknown message "+str(m));
+        }
+	}
+}
+
+
+MidiMessage* MidiMessage::parse(Json::Value& v)
+{
+    if(!v.isArray() || !v.size()) return NULL;
+    std::string _type = v[0].asString();
+    std::string type;
+    int s = _type.size();
+    for(int i=0; i<s; i++) type+=tolower(_type[i]);
+
+    if(type=="noteon" || type=="noteoff")
+    {
+		if(s<4) THROW(MidiParseException,"JsonErreur: NoteOn doit avoir 3 paramètre supplémentaires");
+        Note* n = (type=="noteon")?
+            ((Note*)new NoteOn(-1,-1,-1)):((Note*)new NoteOff(-1,-1,-1));
+        if(!v[1].isNull()) n->channel=v[1].asInt();
+        if(!v[2].isNull()) n->key=v[2].asInt();
+        if(!v[3].isNull()) n->velocity=v[3].asInt();
+        return n;
+    }
+	THROW(MidiParseException, "Uniqument le message NoteOn et NoteOff sont supportés pour MidiMessage::parse(Json::Value& v)");
+    return NULL;
+}

+ 112 - 0
src/MidiPort.cpp

@@ -0,0 +1,112 @@
+#include "MidiPort.h"
+
+#include "MidiMessage.h"
+
+void __RtMidiInCallback(double ts, MidiRawMessage* rm, void *data)
+{
+    MidiPortIn* self = static_cast<MidiPortIn*>(data);
+    self->_handler(ts, rm);
+}
+
+void __RtMidiErrorCallback(RtMidiError::Type type, const std::string &errorText, void *data)
+{
+    MidiPortBase* self = static_cast<MidiPortBase*>(data);
+    self->_error_handler(type, errorText);
+}
+
+
+
+MidiPortIn::MidiPortIn(const std::string &clientName) :
+    RtMidiIn(Api::LINUX_ALSA, clientName, 100),
+    MidiPortBase()
+{
+    m_callback=NULL;
+    m_callback_data=NULL;
+    m_listener=NULL;
+    setCallback(&__RtMidiInCallback, this);
+    setErrorCallback(&__RtMidiErrorCallback, this);
+}
+
+
+void MidiPortIn::_handler(double ts, MidiRawMessage* rm)
+{
+    MidiMessage* m = MidiMessage::parse(*rm);
+    if(m_listener)
+    {
+        m_listener->on_new_message(ts, m);
+        delete m;
+    }else if(m_callback)
+    {
+        m_callback(ts, m, m_callback_data);
+        delete m;
+    }else{
+    }
+}
+
+
+MidiPortBase::MidiPortBase(){
+	m_error_callback=NULL;
+	m_error_callback_data=NULL;
+	m_error_listener=NULL;
+}
+
+void MidiPortBase::_error_handler(RtMidiError::Type type, const std::string &errorText)
+{
+    if(m_error_listener)
+    {
+        m_error_listener->on_error(type, errorText);
+    }else if(m_error_callback)
+    {
+        m_error_callback(type, errorText, m_error_callback_data);
+    }else{
+    }
+}
+
+
+MidiPortOut::MidiPortOut(const std::string &clientName) :
+        RtMidiOut(Api::LINUX_ALSA, clientName), MidiPortBase()
+{
+    setErrorCallback(&__RtMidiErrorCallback, this);
+}
+
+
+void MidiPortOut::send(const MidiMessage& m)
+{
+    uint8_t buffer[3];
+    int n = m.get_bytes(buffer);
+    sendMessage(buffer, n);
+}
+
+
+
+void MidiPortOut::send(const MidiMessageList& m)
+{
+    uint8_t buffer[3];
+    int s = m.size();
+    for(int i=0; i<s; i++)
+    {
+        int n = m[i]->get_bytes(buffer);
+        sendMessage(buffer, n);
+    }
+}
+
+std::vector<std::string> MidiPortIn::get_ports()
+{
+	std::vector<std::string> ret;
+	RtMidiIn in(RtMidiIn::Api::LINUX_ALSA, "--", 100);
+	int s = in.getPortCount();
+	for(int i=0; i<s; i++)
+		ret.push_back(in.getPortName(i));
+	return ret;
+}
+
+
+std::vector<std::string> MidiPortOut::get_ports()
+{
+	std::vector<std::string> ret;
+	RtMidiIn out(RtMidiIn::Api::LINUX_ALSA, "--", 100);
+	int s = out.getPortCount();
+	for(int i=0; i<s; i++)
+		ret.push_back(out.getPortName(i));
+	return ret;
+}

+ 104 - 0
src/Operations.cpp

@@ -0,0 +1,104 @@
+#include "Operations.h"
+#include "PadSelection.h"
+
+
+
+#include "MidiMessage.h"
+
+InputOperation* InputOperation::from_json(Json::Value & v)
+{
+    if(v["type"].asString()=="TRANSLATE")
+        return new TranslationOperation(v);
+    else if(v["type"].asString()=="DROP")
+        return  new DropOperation();
+    else if(v["type"].asString()=="RANGE")
+        return  new RangeOperation(v);
+    else if(v["type"].asString()=="REORDER")
+        return  new ReorderOperation(v);
+
+    return NULL;
+}
+
+TranslationOperation::Mode TranslationOperation::parse_mode(const std::string& v)
+{
+    if(v=="RELATIVE") return TranslationOperation::RELATIVE;
+    if(v=="UNCHANGED") return TranslationOperation::UNCHANGED;
+    if(v=="FIXED") return TranslationOperation::FIXED;
+    return TranslationOperation::RELATIVE;
+}
+
+TranslationOperation::TranslationOperation(Json::Value& v) : InputOperation(TRANSLATION)
+{
+    set(v["values"][0].asInt(), v["values"][1].asInt(), v["values"][2].asInt());
+    set_mode(TranslationOperation::parse_mode(v["modes"][0].asString()),
+            TranslationOperation::parse_mode(v["modes"][1].asString()),
+            TranslationOperation::parse_mode(v["modes"][2].asString()));
+
+}
+
+
+MidiMessage* TranslationOperation::execute(PadSelection* sel, MidiMessage* m, int i) const
+{
+    Note* n=NULL;
+    switch (m->type) {
+        case MidiMessage::NOTE_OFF:
+        case MidiMessage::NOTE_ON:
+        case MidiMessage::CONTROLLER_CHANGE:
+            n=dynamic_cast<Note*>(m);
+            break;
+        default:
+            return m;
+    }
+
+    if(m_channel_mode==RELATIVE) n->channel+=m_channel;
+    else n->channel=m_channel;
+
+    if(m_key_mode==RELATIVE) n->key+=m_key;
+    else n->key=m_key;
+
+    if(m_velocity_mode==RELATIVE) n->velocity+=m_velocity;
+    else n->velocity=m_velocity;
+
+    return m;
+}
+
+MidiMessage* RangeOperation::execute(PadSelection* sel, MidiMessage* m, int i) const
+{
+    Note* n=NULL;
+    switch (m->type) {
+        case MidiMessage::NOTE_OFF:
+        case MidiMessage::NOTE_ON:
+        case MidiMessage::CONTROLLER_CHANGE:
+            n=dynamic_cast<Note*>(m);
+            break;
+        default:
+            return m;
+    }
+
+    int ygamme = (i)/m_range.size();
+    int xgamme = (i)%m_range.size();
+    fprintf(stderr, " %d -> %d\n",n->key, m_key+ygamme*m_length+m_range[xgamme]);
+    n->key =m_key+ygamme*m_length+m_range[xgamme];
+    return n;
+}
+
+ReorderOperation::ReorderOperation(Json::Value& v) : InputOperation(REORDER)
+{
+    m_offset=v["offset"].asInt();
+}
+
+MidiMessage* ReorderOperation::execute(PadSelection* sel, MidiMessage* m, int i) const
+{
+    Note* n=NULL;
+    switch (m->type) {
+        case MidiMessage::NOTE_OFF:
+        case MidiMessage::NOTE_ON:
+        case MidiMessage::CONTROLLER_CHANGE:
+            n=dynamic_cast<Note*>(m);
+            break;
+        default:
+            return m;
+    }
+    n->key=i;
+    return n;
+}

+ 112 - 0
src/Pad.cpp

@@ -0,0 +1,112 @@
+#include "Pad.h"
+#include "Config.h"
+#include "PadManager.h"
+#include "unistd.h"
+#include "PadConfiguration.h"
+#include "PadDefinition.h"
+
+
+Pad::Pad()
+{
+   m_configuration=NULL;
+   m_pad_definition=NULL;
+}
+
+Pad::Pad(const std::string& file)
+{
+	m_configuration=NULL;
+	m_pad_definition=NULL;
+	if(!load_configuration(file)){
+		THROW(UnknownError, "Erreur dans le chargement du Pad");
+	}
+}
+
+Pad::Pad(PadDefinition* p)
+{
+	m_configuration=NULL;
+	m_pad_definition=p;
+	m_name=p->get_name();
+}
+
+Pad::Pad(PadConfiguration* p)
+{
+	m_configuration=p;
+	m_pad_definition=&p->get_pad_definition();
+	m_name=m_pad_definition->get_name();
+}
+
+Pad::~Pad()
+{
+	if(m_configuration) delete m_configuration;
+}
+
+void Pad::remove_configuration()
+{
+    delete m_configuration;
+    m_configuration=NULL;
+}
+
+bool Pad::load_configuration(const std::string& file)
+{
+    if(m_configuration){
+        remove_configuration();
+    }
+    m_configuration = PadConfiguration::from_file(file);
+    if(!m_configuration){
+		THROW(UnknownError,"Impossible de charger le pad...");
+    }
+	PadDefinition * newpad = &m_configuration->get_pad_definition();
+
+	if(m_pad_definition && m_pad_definition!=newpad){
+		THROW(UnknownError,"Impossible de charger le pad...");
+    }
+	m_pad_definition = newpad;
+
+    if(Config::get_bool("autoconnect")){
+		connect(
+				!m_pad_definition->get_input_port().isPortOpen(),
+				!m_pad_definition->get_control_port().isPortOpen());
+    }
+
+    if(Config::get_bool("clear")){
+		m_pad_definition->clear();
+        //usleep(100000);
+    }
+    m_configuration->send_init();
+    return true;
+}
+
+void Pad::connect(bool in, bool out)
+{
+	m_pad_definition->connect_to_device(in, out);
+}
+
+
+const std::string& Pad::get_name() const
+{
+	return m_name;
+}
+
+
+void Pad::clear()
+{
+	m_pad_definition->clear();
+}
+void Pad::input(MidiMessageList* m)
+{
+	input(m);
+}
+
+void Pad::input(Json::Value& msg){
+	MidiMessageList* m = MidiMessageList::parse(msg);
+	input(m);
+	m->free_content();
+}
+
+void Pad::input(const std::string& msg){
+	MidiMessageList* m = MidiMessageList::parse(msg);
+	input(m);
+	m->free_content();
+}
+
+

+ 158 - 0
src/PadConfiguration.cpp

@@ -0,0 +1,158 @@
+#include <unistd.h>
+#include "PadConfiguration.h"
+#include "PadSelection.h"
+#include <fstream>
+#include "PadDefinition.h"
+#include "PadManager.h"
+/**
+
+    TRANSLATE = {
+        "type" : "TRANSLATE",
+        "values" : (int, int, int) # les valeur des channels, key et velocity
+        "modes" : (str, str, str) # les modes des channels, key et velocity parmi ("RELATIVE" ou "FIXED")
+    }
+
+    DROP = {
+        type : "DROP"
+    }
+
+    REORDER = {
+        "type" : "REORDER",
+        "offset" : int
+    }
+
+    RANGE = {
+        type : "RANGE",
+        "range" : [ 0,1 ,2,3] #les note %12
+        "key" : int # note midi de départ
+        "length" : longeuer de la gamme (souvent 12
+    }
+
+    SELECTION = {
+        "operations" : [
+            {TRANSLATE | DROP | RANGE}
+        ],
+        "init": [
+            action,
+        ],
+        "locked" ; [ (x,y) ]
+    }
+
+
+
+    {
+        "name" : string
+        "description" string
+        "methods" : {
+            "clear" : INPUT_ACTION
+        }
+        "pad" : string # Nom du pad sur laquelle la configuration peut fonctionner
+        "selections" : [
+            {SELECTION}
+        ],
+        "matrix" : [string] # le nom de la sélection correspondant à chaque bouton
+    }
+*/
+
+PadConfiguration::PadConfiguration(Json::Value& v) :
+	_m_pad_definition_ptr(PadManager::get_pad(v["pad"].asString())),
+	m_pad_definition(*_m_pad_definition_ptr),
+	m_matrix(m_pad_definition.m_matrix)
+{
+	m_pad_definition.set_error_listener(this);
+	m_pad_definition.set_port_listener(this);
+	m_iport=m_pad_definition.m_iport;
+	m_cport=m_pad_definition.m_cport;
+	m_oport=m_pad_definition.m_oport;
+	m_width=m_pad_definition.m_width;
+	m_height=m_pad_definition.m_height;
+	m_matrix=m_pad_definition.m_matrix;
+    m_name=v["name"].asString();
+    m_description=v["description"].asString();
+    Json::Value sel = v["selections"], mat=v["matrix"];
+    int i, s = sel.size();
+
+    for( Json::Value::const_iterator itr = sel.begin() ; itr != sel.end() ; itr++ ) {
+        std::string k = itr.key().asString();
+        Json::Value v = *itr;
+        m_selections.push_back(new PadSelection(k, v, this));
+    }
+
+    for(int i=0; i<m_width*m_height; i++)
+    {
+        if(mat[i].isString())
+        {
+            PadSelection* sel = selection_by_name(mat[i].asString());
+            if(m_matrix[i])sel->add(m_matrix[i]);
+        }
+    }
+}
+
+PadDefinition& PadConfiguration::get_pad_definition()
+{
+	return m_pad_definition;
+}
+
+
+
+void PadConfiguration::send_init()
+{
+    usleep(10000);
+    int s=m_selections.size();
+    for(int i=0; i<s; i++)
+        m_selections[i]->send_init();
+}
+
+PadConfiguration::~PadConfiguration()
+{
+    int s = m_selections.size();
+    for(int i=0; i<s; i++)
+        delete m_selections[i];
+}
+
+
+void PadConfiguration::on_error(RtMidiError::Type type, const std::string &errorText)
+{
+    std::cerr << "Midi Erreur : " << errorText << "\n";
+}
+
+
+void PadConfiguration::on_new_message(double ts, MidiMessage *m)
+{
+	AbsInput* in = m_pad_definition.get_by_message(m);
+    PadSelection* sel = in->get_selection();
+    if(sel)
+    {
+        m=sel->execute(in, m);
+        if(m)m_oport->send(m);
+    }else{
+        m_oport->send(m);
+    }
+}
+
+PadSelection* PadConfiguration::selection_by_name(const std::string& n)
+{
+    int s = m_selections.size();
+    for(int i=0; i<s; i++)
+        if(m_selections[i]->get_name()==n)
+            return m_selections[i];
+   return NULL;
+}
+
+PadConfiguration* PadConfiguration::from_file(const std::string& str)
+{
+    std::ifstream  fd(str);
+    if(!fd.is_open()){
+		THROW(NotFoundError, "Le fichier de configuration de pad '"<< str << "' est inaccessible");
+    }
+
+    std::string x;
+    Json::Value val;
+    try{
+        fd >> val;
+    }catch(...){
+		THROW(ParseError, "Le fichier de configuration de pad '"<< str << "' est malformé (json invaide)");
+    }
+    fd.close();
+    return new PadConfiguration(val);
+}

+ 334 - 0
src/PadDefinition.cpp

@@ -0,0 +1,334 @@
+#include "PadDefinition.h"
+#include <fstream>
+
+#include "InputDefinition.h"
+
+#include "Button.h"
+#include "Controller.h"
+
+AbsInput* __pad_default_button(PadDefinition* p, void * data)
+{
+    return new Button(p);
+}
+
+
+AbsInput* __pad_default_controller(PadDefinition* p, void * data)
+{
+    return new Controller(p);
+}
+
+Button* PadDefinition::instanciate_new_button() const
+{
+	return dynamic_cast<Button*>(m_create_button((PadDefinition*)this, m_create_button_data));
+}
+
+Controller* PadDefinition::instanciate_new_controller() const
+{
+	return dynamic_cast<Controller*>(m_create_controller((PadDefinition*)this, m_create_controller_data));
+}
+
+
+AbsInput* AbsInput::from_json(PadDefinition* p, Json::Value& v)
+{
+    if(v.isObject())
+    {
+        std::string s = v["type"].asString();
+        InputDefinition* id = p->get_input_def(s);
+        if(!id)
+			 THROW(NotFoundError,"JsonErreur: InputDefinition '"+s+"' est inconnue");
+        return id->instanciate(p, v);
+    }
+    else if(v.isNull()) return NULL;
+	THROW(ParseError, "Erreur les données JSon n'est pas un objet");
+}
+
+std::vector<AbsInput*>& PadDefinition::get_matrix()
+{
+    return m_matrix;
+}
+
+
+PadDefinition::PadDefinition()
+{
+    m_iport=new MidiPortIn(m_name+" Controller");
+    m_cport=new MidiPortOut(m_name+" Controller");
+    m_oport=new MidiPortOut(m_name+" Controller");
+    m_iport->set_listener(this);
+    m_iport->openVirtualPort("Device in");
+    m_cport->openVirtualPort("Device out");
+    m_oport->openVirtualPort("Midi Out");
+    m_oport_open=true;
+    m_height=m_width;
+    m_error_listener=NULL;
+    m_port_listener=NULL;
+    m_create_button=__pad_default_button;
+    m_create_controller=__pad_default_controller;
+    m_create_button_data=m_create_controller_data=NULL;
+}
+/**
+    ACTIONS_DEFINITION -> [message : string, args, ...] | [[message : string, args, ...], ...]
+
+    INPUT_DEFINTIONS -> {
+            "type": string # "BUTTON" or "CONTROLLER"
+            "actions" : {
+                {action_id}: ACTIONS_DEFINITION
+            }
+            "shape" : string # Name of shape //to implement
+        }
+
+    INPUT -> {
+            "type": string # input_defintion_id,
+            "channel" : int # fixed channel or -1 (to replace)
+            "key" : int # fixed key or -1 (to replace)
+            "velocity" : int # fixed velocity or -1 (to replace)
+            "locked" : string or null # @action_id
+        }
+
+
+
+    PAD -> {
+        "name" : string #name of pad
+        "width": int #width of matrix input,
+        "height": int #height of matrix input,
+        "ports": {
+            "input": string #Name of input port
+            "output": string #Name of output port
+        },
+        "inputs" : {
+            {input_defintion_id_N} : INPUT_DEFINTIONS
+        }
+        "matrix" : [INPUT, INPUT, INPUT, ..... ] # x*y
+    }
+*/
+#include "PadManager.h"
+
+PadDefinition::PadDefinition(Json::Value v)
+{
+    read_conf(v);
+}
+PadDefinition::PadDefinition(const std::string& v)
+{
+    if(!PadManager::has_pad(v))
+    {
+		THROW(NotFoundError, "Pad '"+v+"' est introuvable");
+    }
+    Json::Value pad = PadManager::read_pad(v);
+    read_conf(pad);
+}
+
+void PadDefinition::read_conf(const std::string& file)
+{
+    std::ifstream  fd(std::string(PAD_DEFINITION_DIR)+"/"+file);
+	Json::Value val;
+    fd >> val;
+    read_conf(val);
+}
+
+void PadDefinition::clear()
+{
+    send_all("clear");
+}
+
+void PadDefinition::send_all(const std::string& name)
+{
+    int l=m_width*m_height;
+    for(int i=0; i<l; i++){
+        AbsInput* input = m_matrix[i];
+        if(input){
+            input->control(name, true);
+        }
+    }
+}
+
+void PadDefinition::read_conf(Json::Value& v)
+{
+	if(!v.isObject()) THROW(MalformedError, "JSonErreur: Pad doit être un objet");
+
+    m_error_listener=NULL;
+    m_port_listener=NULL;
+    m_name = v["name"].asString();
+    m_width = v["width"].asInt();
+    m_height = v["height"].asInt();
+    m_port_in_name = v["ports"]["input"].asString();
+    m_port_out_name = v["ports"]["output"].asString();
+    m_iport=new MidiPortIn(m_name+" Controller");
+    m_cport=new MidiPortOut(m_name+" Controller");
+    m_oport=new MidiPortOut(m_name+" Controller");
+    m_iport->set_listener(this);
+    m_iport->openVirtualPort("Device in");
+    m_cport->openVirtualPort("Device out");
+    m_oport->openVirtualPort("Midi Out");
+
+
+    Json::Value root = v["inputs"];
+    for( Json::Value::const_iterator itr = root.begin() ; itr != root.end() ; itr++ ) {
+        std::string k = itr.key().asString();
+		Json::Value v = *itr;
+        m_input_def.push_back(new InputDefinition(k,v));
+
+    }
+    m_create_button=__pad_default_button;
+    m_create_controller=__pad_default_controller;
+    m_create_button_data=m_create_controller_data=NULL;
+    root = v["matrix"];
+    int s = root.size();
+    for(int i=0; i<s; i++)
+    {
+        AbsInput* b = AbsInput::from_json(this, root[i]);
+        if(b)
+        {
+            b->x = i%m_width;
+            b->y = i/m_width;
+            b->index = i;
+        }
+        m_matrix.push_back(b);
+    }
+}
+
+PadDefinition::~PadDefinition()
+{
+    delete m_iport;
+    delete m_cport;
+    delete m_oport;
+}
+
+
+InputDefinition* PadDefinition::get_input_def(const std::string& s)
+{
+    int size = m_input_def.size();
+    for(int i=0; i<size; i++)
+    {
+        if(m_input_def[i]->m_name==s) return m_input_def[i];
+    }
+    return NULL;
+}
+
+void PadDefinition::connect_to_device(bool input, bool output)
+{
+    int nport;
+    std::string portName;
+
+    if(input && !m_iport_open){
+        nport=m_iport->getPortCount();
+        for(int i=0; i<nport; i++)
+        {
+            portName = m_iport->getPortName(i);
+            if(start_with(portName, m_port_in_name))
+            {
+				//std::cout << "Find input port : "<<portName << "\n";
+                m_iport->openPort(i, "Device in");
+                m_iport_open=true;
+                break;
+            }
+        }
+    }
+    if(output && !m_cport_open){
+        nport = m_cport->getPortCount();
+        for(int i=0; i<nport; i++)
+        {
+            portName = m_cport->getPortName(i);
+            if(start_with(portName, m_port_out_name))
+            {
+                std::cout << "Find output port : "<<portName << "\n";
+                m_cport->openPort(i, "Device out");
+                m_cport_open=true;
+                break;
+            }
+        }
+    }
+}
+
+void PadDefinition::set_error_listener(IMidiPortErrorListener * p)
+{
+    m_error_listener=p;
+}
+
+void PadDefinition::set_port_listener(IMidiPortListener * p )
+{
+    m_port_listener=p;
+}
+
+void PadDefinition::on_error(RtMidiError::Type type, const std::string &errorText)
+{
+    if(m_error_listener){
+        m_error_listener->on_error(type, errorText);
+    }
+    else
+    {
+        std::cerr << "Erreur : " << errorText << "\n";
+		THROW(MidiError, "Erreur Midi: "+errorText);
+    }
+}
+
+void PadDefinition::on_new_message(double ts, MidiMessage* m)
+{
+    if(m_port_listener){
+        m_port_listener->on_new_message(ts, m);
+    }
+    else
+    {
+        _dispatch_message(ts, m);
+    }
+}
+
+void PadDefinition::_dispatch_message(double ts, MidiMessage* m)
+{
+    AbsInput* in = get_by_message(m);
+    if(in) in->on_input(ts, m);
+
+}
+
+AbsInput* PadDefinition::get_by_message(const MidiMessage* msg)
+{
+    InputType type = BUTTON;
+    switch(msg->type)
+    {
+        case MidiMessage::CONTROLLER_CHANGE:
+            type = CONTROLLER;
+        case MidiMessage::NOTE_ON:
+        case MidiMessage::NOTE_OFF:
+        {
+            const Note *n = dynamic_cast<const Note*>(msg);
+            return get_by_channel_key(type, n->channel, n->key);
+        }
+
+    }
+    return NULL;
+}
+
+AbsInput* PadDefinition::input_at(int x, int y)
+{
+    if(y<0) return m_matrix[x];
+    return m_matrix[y*m_width+x];
+}
+
+AbsInput* PadDefinition::get_by_channel_key(InputType type, int channel, int key)
+{
+    int s = m_matrix.size();
+    for(int i=0; i<s; i++)
+    {
+        AbsInput* b = m_matrix[i];
+
+        if(b && b->type==type && b->realchannel==channel && b->realkey==key)
+            return b;
+    }
+    return NULL;
+}
+
+PadDefinition* PadDefinition::from_file(const std::string& str)
+{
+    std::ifstream  fd(str);
+    Json::Value val;
+    printf("IsOpen : %d\n", fd.is_open());
+    fd >> val;
+	return new PadDefinition(val);
+}
+
+PadDefinition* PadDefinition::from_name(const std::string& str)
+{
+    std::ifstream  fd(std::string(PAD_DEFINITION_DIR)+"/"+str);
+    Json::Value val;
+    printf("IsOpen : %d\n", fd.is_open());
+    fd >> val;
+	return new PadDefinition(val);
+}

+ 141 - 0
src/PadManager.cpp

@@ -0,0 +1,141 @@
+#include "PadManager.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+PadManager* PadManager::m_instance=NULL;
+#include <fstream>
+#include "Config.h"
+#include "PadDefinition.h"
+
+PadEntry::~PadEntry()
+{
+	if(instance) delete instance;
+}
+
+void PadManager::_init()
+{
+    std::string dir = Config::PAD_DIR();
+    DIR* dirp = opendir(dir.c_str());
+    struct dirent * dp;
+    while ((dp = readdir(dirp)) != NULL) {
+        std::string path=Config::PAD_DIR(dp->d_name);
+        struct stat spath;
+
+        stat(path.c_str(), &spath);
+        try{
+            std::ifstream fd(path);
+            if(fd.is_open())
+            {
+                Json::Value v;
+                fd >> v;
+				if(v.isObject()){
+					std::string name, in, out;
+					if(v.isMember("name") && v["name"].isString())
+						name=v["name"].asString();
+					if(v.isMember("ports") && v["ports"].isObject()){
+						Json::Value ports = v["ports"];
+						if(ports.isMember("input") && ports["input"].isString())
+							in=ports["input"].asString();
+						if(ports.isMember("output") && ports["output"].isString())
+							out=ports["output"].asString();
+					}
+					if(name!="" && in!="" && out!=""){
+						PadEntry& p = *_add();
+						p.name=name;
+						p.in_port=in;
+						p.out_port;
+						p.filename=dp->d_name;
+					}else{
+						std::cerr << "Attention pad '"<< name <<"' ("<< path <<") est malformé\n";
+					}
+				}
+                fd.close();
+            }
+        }catch(...){
+
+        }
+    }
+    closedir(dirp);
+}
+
+int PadManager::_get_pad_index(const std::string& name)
+{
+	int i=0, l=m_list.size();
+    for(i=0; i<l; i++)
+		if(name==m_list[i]->name)
+            return i;
+    return -1;
+}
+
+PadDefinition* PadManager::_get_pad(const std::string& name)
+{
+    int i = _get_pad_index(name);
+	if(!m_list[i]->instance)
+    {
+		PadDefinition* p = new PadDefinition(_read_pad(name));
+		m_list[i]->instance=p;
+    }
+	return m_list[i]->instance;
+}
+
+bool PadManager::_has_pad(const std::string& pad_name) const
+{
+    int s=m_list.size();
+    for(int i=0; i<s; i++)
+		if(m_list[i]->name==pad_name)
+           return true;
+    return false;
+}
+
+Json::Value PadManager::_read_pad(const std::string &pad_name) const
+{
+    std::string filename;
+    int s=m_list.size();
+    for(int i=0; i<s; i++){
+		if(m_list[i]->name==pad_name)
+        {
+		   filename=m_list[i]->filename;
+           break;
+        }
+    }
+    if(filename==""){
+		THROW(NotFoundError, "Impossible de trouver le pad '" << pad_name << "'");
+    }
+    filename=Config::PAD_DIR(filename);
+    std::ifstream fd(filename);
+    if(fd.is_open()){
+        try {
+            Json::Value v;
+            fd >> v;
+            fd.close();
+            return v;
+        }  catch (...) {
+            fd.close();
+            std::cerr << "Erreur le fichier du pad '"<<pad_name<<"' ('"<<filename<<"')\n";
+            return Json::Value();
+        }
+
+    }else{
+        std::cerr << "Impossible de lire le fichier du pad '"<<pad_name<<"' ('"<<filename<<"')\n";
+        return Json::Value();
+    }
+}
+
+PadEntry* PadManager::_add()
+{
+	static int _max_id=-1;
+	PadEntry* p = new PadEntry(++_max_id);
+	m_list.push_back(p);
+	return p;
+}
+
+void PadManager::_clean()
+{
+	int i, s=m_list.size();
+	for(i=0; i<s; i++){
+		delete m_list[i];
+	}
+	m_list.clear();
+}

+ 195 - 0
src/PadSelection.cpp

@@ -0,0 +1,195 @@
+#include "PadSelection.h"
+#include "PadDefinition.h"
+
+#include "Button.h"
+#include "Controller.h"
+#include "Operations.h"
+#include "PadConfiguration.h"
+#include <algorithm>
+
+InputInit::~InputInit()
+{
+    int s = actions.size();
+    for(int i=0; i<s; i++) delete actions[i];
+}
+
+
+
+PadSelection::PadSelection(const std::string& name, Json::Value& v, PadConfiguration* p) : m_pad(p)
+{
+    m_name=name;
+	m_width=p->get_pad_definition().get_width();
+	m_height=p->get_pad_definition().get_height();
+    m_matrix.resize(m_width*m_height);
+    for(int i=0; i<m_width*m_height; i++) m_matrix[i]=NULL;
+    _update_matrix();
+    Json::Value a = v["operations"];
+    int s = a.size();
+    for(int i=0; i<s; i++)
+    {
+        m_operations.push_back(InputOperation::from_json(a[i]));
+    }
+    m_init.add( v["init"]);
+}
+
+
+void PadSelection::send_init()
+{
+    int s = m_list.size();
+    for(int i=0; i<s; i++)
+    {
+        MidiMessageList* msg = m_init.get_messages();
+        m_list[i]->control(msg);
+    }
+}
+
+PadSelection::~PadSelection()
+{
+    int s = m_operations.size();
+    for(int i=0; i<s; i++)
+        delete m_operations[i];
+}
+
+void PadSelection::_update_matrix()
+{
+    m_list.clear();
+    for(int j=m_height-1; j>=0; j--)
+    {
+        for(int i=0; i<m_width; i++)
+        {
+            AbsInput* b = m_matrix[j*m_width+i];
+            if(b) m_list.push_back(b);
+        }
+    }
+}
+
+static bool compare(AbsInput* a, AbsInput* b){
+    return a->index<b->index;
+}
+
+void PadSelection::add(int i, int j)
+{
+    int x = i+j*m_width;
+	AbsInput* p = m_pad->get_pad_definition().at(i,j);
+    if(!m_matrix[x]){
+        m_list.push_back(p);
+    }
+	m_matrix[x]=m_pad->get_pad_definition().at(i,j);
+    m_matrix[x]->set_selection(this);
+    std::sort(m_list.begin(), m_list.end(), compare);
+}
+
+AbsInput* PadSelection::at(int i, int j)
+{
+    return m_matrix[i+j*m_width];
+}
+
+void PadSelection::add(AbsInput* b)
+{
+    return add(b->x, b->y);
+}
+
+void PadSelection::remove(int i, int j)
+{
+	int x = i+j*m_pad->get_pad_definition().get_width();
+    m_matrix[x]=NULL;
+    _update_matrix();
+    m_matrix[x]->set_selection(this);
+
+}
+
+void PadSelection::remove(AbsInput* b)
+{
+    return remove(b->x, b->y);
+}
+
+bool PadSelection::has(int x, int y) const
+{
+    return m_matrix[x+y*m_width];
+}
+
+bool PadSelection::has(AbsInput* p) const
+{
+    return has( p->x, p->y);
+}
+
+
+int PadSelection::count_x(int x) const{
+    int s=m_list.size(), count=0;
+    for(int i=0; i<s; i++)
+        if(m_list[i]->x==x)
+            count++;
+    return count;
+}
+
+int PadSelection::count_y(int y) const{
+    int s=m_list.size(), count=0;
+    for(int i=0; i<s; i++)
+        if(m_list[i]->y==y)
+            count++;
+    return count;
+}
+
+int PadSelection::index_at_x(int absindex) const
+{
+    int x = absindex%m_width;
+    int y = absindex/m_width;
+    int s=m_list.size(), count=0;
+    for(int i=0; i<s; i++)
+        if(m_list[i]->y==y && m_list[i]->x<x)
+            count++;
+    return count;
+}
+
+int PadSelection::index_at_y(int absindex) const
+{
+    int x = absindex%m_width;
+    int y = absindex/m_width;
+    int s=m_list.size(), count=0;
+    for(int i=0; i<s; i++)
+        if(m_list[i]->y==y && m_list[i]->x<x)
+            count++;
+    return count;
+}
+
+
+
+/*
+MidiMessage* PadSelection::execute(AbsInput* in, MidiMessage* m) const
+{
+    int s = m_list.size();
+    for(int i=0; i<s; i++){
+        if(m_list[i]==in){
+            int s = m_operations.size();
+            for(int j=0; j<s; j++)
+            {
+                if(!m_operations[j]->execute((PadSelection*)this, m, i)) return NULL;
+            }
+            return m;
+        }
+    }
+    return m;
+}*/
+MidiMessage* PadSelection::execute(AbsInput* in, MidiMessage* m) const
+{
+    int s = m_list.size(), i, count=0;
+    for(int y=m_height-1; y>=in->y; y--){
+        for(int x=0; x<m_width; x++){
+            i=x+y*m_width;
+            if(m_matrix[i]){
+                if(m_matrix[i]==in){
+                    int s = m_operations.size();
+                    for(int j=0; j<s; j++)
+                    {
+                        if(!m_operations[j]->execute((PadSelection*)this, m, count)) return NULL;
+                    }
+                    return m;
+                }
+                else{
+                    count++;
+                }
+            }
+        }
+    }
+    return m;
+}

+ 252 - 0
src/Socket.cpp

@@ -0,0 +1,252 @@
+#include "Socket.h"
+#include <iostream>
+#include <cstring>
+#include "utils.h"
+#include <cstdlib>
+#include <cstdio>
+#include "Command.h"
+#include "errno.h"
+_AbsSocket::_AbsSocket(SocketType type)
+{
+    m_type=type;
+    m_is_open=false;
+    m_sock = socket(AF_INET, SOCK_STREAM, 0);
+    if(m_sock == INVALID_SOCKET)
+	{
+		THROW(SocketError, "_AbsSocket::_AbsSocket() socket creation " << strerror(errno));
+    }
+}
+
+_AbsSocket::~_AbsSocket()
+{
+   close();
+}
+
+void _AbsSocket::close()
+{
+	if(m_is_open)
+    {
+        ::close(m_sock);
+        m_is_open=false;
+    }
+}
+
+bool _AbsSocket::is_open(){
+	int error_code;
+	socklen_t error_code_size = sizeof(error_code);
+	getsockopt(m_sock, SOL_SOCKET, SO_ERROR, &error_code, &error_code_size);
+	return error_code==0;
+}
+
+std::string& ISocket::readline(std::string & out)
+{
+    int c;
+    while((c=readc())!='\n' && c>=0);
+    return out;
+}
+
+void Socket::write(const void* data, int size)
+{
+    int i=0;
+	while(size>0){
+        int ret = ::send(m_sock, data, size, 0);
+        if(ret < 0)
+		{
+			THROW(SendSocketError, "Socket::write(const void*, int)" << strerror(errno));
+        }
+        size-=ret;
+    }
+}
+
+int Socket::read(void* data, int n, bool blocking)
+{
+    int readed = 0;
+	while( readed<n){
+        int ret;
+        if((ret = recv(m_sock, data, n, 0)) < 0)
+		{
+			THROW(RecieveSocketError, "Socket::read(void*, int, bool)" << strerror(errno));
+		}
+        readed+=ret;
+		if(readed<n && !blocking) break;
+    }
+    return readed;
+}
+
+bool InetSocket::connect(const std::string& addr, int port)
+{
+    struct hostent *hostinfo = NULL;
+    SOCKADDR_IN sin = { 0 }; /* initialise la structure avec des 0 */
+
+
+    hostinfo = gethostbyname(addr.c_str()); /* on récupère les informations de l'hôte auquel on veut se connecter */
+    if (hostinfo == NULL) /* l'hôte n'existe pas */
+	{
+		THROW(HostSocketError, "InetSocket::connect(const std::string&, int) : hostname error" << strerror(errno));
+    }
+
+    sin.sin_addr = *(IN_ADDR *) hostinfo->h_addr; /* l'adresse se trouve dans le champ h_addr de la structure hostinfo */
+    sin.sin_port = htons(port); /* on utilise htons pour le port */
+    sin.sin_family = AF_INET;
+
+    if(::connect(m_sock,(SOCKADDR *) &sin, sizeof(SOCKADDR)) == SOCKET_ERROR)
+	{
+		THROW(ConnectSocketError, "InetSocket::connect(const std::string&, int)" << strerror(errno));
+    }
+    m_is_open=true;
+    return true;
+}
+
+InetServer::InetServer() : _AbsSocket(INET)
+{
+	m_sock = socket(AF_INET, SOCK_STREAM, 0);
+    if(m_sock == INVALID_SOCKET)
+	{
+		THROW(SocketError, "InetSocket::InetServer() : créatiopn de socket" << strerror(errno));
+    }
+}
+
+ISocket* InetServer::accept()
+{
+    SOCKADDR_IN csin = { 0 };
+    SOCKET csock;
+    int sinsize = sizeof csin;
+
+    csock = ::accept(m_sock, (SOCKADDR *)&csin, (socklen_t*)&sinsize);
+
+    if(csock == INVALID_SOCKET)
+	{
+		THROW(SocketError, "InetServer::accept()" << strerror(errno));
+    }
+	return new InetSocket(csock);
+}
+
+int _getc()
+{
+	int x;
+	x=getc(stdin);
+	printf("%d\n", x);
+	return x;
+}
+
+ISocket* StdioServer::accept(){
+	int x = 0;
+	for(x=getc(stdin); x<0; x=getc(stdin))
+		usleep(100000);
+
+	return new StdioSocket((char)x);
+}
+
+void InetServer::listen(int port)
+{
+    SOCKADDR_IN sin = { 0 };
+	int yes = 1;
+	int ret;
+    sin.sin_addr.s_addr = htonl(INADDR_ANY); /* nous sommes un serveur, nous acceptons n'importe quelle adresse */
+
+    sin.sin_family = AF_INET;
+
+    sin.sin_port = htons(port);
+
+	if ( ret = setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &yes, sizeof(yes))) {
+		THROW(OptSocketError, "InetSocket::listen()" << strerror(errno));
+	}
+
+	if(ret = bind (m_sock, (SOCKADDR *) &sin, sizeof sin))
+    {
+		THROW(BindSocketError, "InetSocket::listen()" << strerror(errno));
+	}
+	if(ret = ::listen(m_sock, 5))
+	{
+		THROW(ListenSocketError, "InetSocket::listen()" << strerror(errno));
+    }
+}
+
+bool UnixSocket::connect(const std::string& path)
+{
+	struct sockaddr_un addr;
+	memset(&addr, 0, sizeof(struct sockaddr_un));
+	addr . sun_family = AF_UNIX ;
+	strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path)-1);
+	if(::connect(m_sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)))
+	{
+		THROW(ConnectSocketError, "UnixSocket::connect(const std::string&)" << strerror(errno));
+	}
+	return true;
+
+}
+
+UnixServer::UnixServer() : _AbsSocket(UNIX)
+{
+
+    m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if(m_sock == INVALID_SOCKET)
+	{
+		THROW(SocketError, "UnixServer::UnixServer()" << strerror(errno));
+    }
+}
+
+ISocket* UnixServer::accept()
+{
+    struct sockaddr_un addr;
+    SOCKET csock;
+    memset(&addr , 0 , sizeof ( struct sockaddr_un ) ) ;
+    int sinsize = sizeof addr;
+
+    csock = ::accept(m_sock, (SOCKADDR *)&addr, (socklen_t*)&addr);
+
+    if(csock == INVALID_SOCKET)
+	{
+		THROW(AcceptSocketError, "UnixSocket::accept()" << strerror(errno));
+    }
+    return new Socket(m_type, csock);
+}
+
+void UnixServer::listen(const std::string& path, bool force)
+{
+    struct sockaddr_un addr;
+	memset(&addr, 0, sizeof(struct sockaddr_un ));
+    addr.sun_family = AF_UNIX ;
+    strncpy( addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+    m_path=path;
+    if(Fs::is_dir(m_path))
+	{
+		THROW(ListenSocketError, "UnixSocket::listen(const std::string&, bool)" << strerror(errno));
+    }
+    if(Fs::is_file(m_path) && force)
+        Fs::remove(m_path);
+    if(bind (m_sock, (SOCKADDR *) &addr, sizeof addr) == SOCKET_ERROR)
+	{
+		THROW(BindSocketError, "UnixSocket::listen(const std::string&, bool)" << strerror(errno));
+    }
+    if(::listen(m_sock, 5) == SOCKET_ERROR)
+    {
+		THROW(ListenSocketError, "UnixSocket::listen(const std::string&, bool)" << strerror(errno));
+    }
+}
+
+UnixServer::~UnixServer()
+{
+    close();
+    Fs::remove(m_path);
+}
+
+
+void StdioSocket::write(const void* data, int size)
+{
+	fwrite(data, 1, size, stderr);
+}
+
+
+int  StdioSocket::read(void* data, int n, bool blocking)
+{
+	int m=0;
+	if(m_first>=0)
+	{
+		((char*)data)[0]=(char)m_first;
+		m_first=-1;
+		m++;
+	}
+	return fread(data, 1, n-m, stdin)+m;
+}

+ 123 - 0
src/commands.cpp

@@ -0,0 +1,123 @@
+#include "Application.h"
+#include "Pad.h"
+#include "Socket.h"
+#include "Command.h"
+
+Command::CommandReturn _help(Application& app, Pad& pad, std::vector<Json::Value>& args, CommandLog& io)
+{
+	app.help(io);
+	return Command::CONTINUE;
+}
+
+Command::CommandReturn _(Application& app, Pad& pad, std::vector<Json::Value>& args, CommandLog& io)
+{
+	return Command::CONTINUE;
+}
+
+Command::CommandReturn _exit(Application& app, Pad& pad, std::vector<Json::Value>& args, CommandLog& io)
+{
+	return Command::EXIT;
+}
+
+Command::CommandReturn _load(Application& app, Pad& pad, std::vector<Json::Value>& args, CommandLog& io)
+{
+	if(!pad){
+		THROW(NoPadSelectedError, "");
+	}
+	if(args.size()<2 || !args[0].isString() || !args[1].isString()){
+		THROW(ArgumentCommandError, "Pad not loaded");
+	}
+	if(args[0].asString()=="conf" || args[0].asString()=="configuration"){
+		if(app.load_pad_configuration(args[1].asString())){
+			io << "Configuration '"<< args[0].asString() << "' chargée\n";
+		}
+	}
+	else if(args[0].asString()=="def" || args[0].asString()=="definition"){
+		if(app.load_pad_definition(args[1].asString())){
+			io << "Pad '"<< args[0].asString() << "' chargé\n";
+		}
+	}else{
+		THROW(ArgumentCommandError, "Pad not loaded");
+	}
+	return Command::CONTINUE;
+}
+
+Command::CommandReturn _close(Application& app, Pad& _pad, std::vector<Json::Value>& args, CommandLog& io)
+{
+	return Command::CLOSE;
+}
+
+Command::CommandReturn _pad(Application& app, Pad& _pad, std::vector<Json::Value>& args, CommandLog& io)
+{
+	int l = args.size();
+	if(!l) THROW(ArgumentCommandError, "");
+	unsigned int i=0;
+	Json::Value v = args[0];
+	std::string cmd;
+	Pad* pad = &_pad;
+	if(v.isInt()){
+		pad=&app.get_pad(v.asInt());
+		i++;
+	}
+	cmd=args[i].asString();
+	i++;
+	if(cmd=="new" && args.size()>=i+1 && args[i].isString()){
+		if(app.load_pad_definition(args[i].asString())){
+			io << "Pad '"<< args[i].asString() << "' chargé\n";
+		}else{
+			io <<  "Impossible de charger le pad '"<< args[i].asString() <<"'\n";
+		}
+	}else if(cmd=="load" && args.size()>=i+1 && args[i].isString()){
+		if(app.load_pad_configuration(args[i].asString())){
+			io <<  "Configuration '"<< args[i].asString() << "' chargée\n";
+		}else{
+			io <<  "Impossible de charger la configuration '"<< args[i].asString() <<"'\n";
+		}
+	}else if(cmd=="input" && args.size()>=i+1 && args[i].isString() ){
+		if(!pad)THROW(NoPadSelectedError, "");
+		pad->input(args[i].asString());
+	}else if(cmd=="remove" && args.size()>=i+1 && args[i].isInt()){
+		if(!pad)THROW(NoPadSelectedError, "");
+		app.remove_pad(args[i].asInt());
+	}else if(cmd=="list"){
+		int len = app.pad_count();
+		io <<  len << " pads:\n";
+		for(int i=0; i<len; i++){
+			Pad& p = app[i];
+			if(&p){
+				io <<  "\t" << ((&p==&_pad)?(">"):(" ")) << app.get_id(p.get_name())
+					 << " : " <<  p.get_name() <<"\n" ;
+			}
+		}
+	}else if(cmd=="select" && args.size()>=i+1 && (args[i].isInt() || args[i].isString())){
+		if(args[i].isInt()) app.set_pad(args[i].asInt());
+		else app.set_pad(args[i].asString());
+	}else if(cmd=="clear"){
+		pad->clear();
+	}else{
+		THROW(ArgumentCommandError, "Erreur: argmunents non reconnus...")
+	}
+
+	return Command::CONTINUE;
+}
+
+void add_commands(Application& app)
+{
+	app.add_command("help", _help, "Affiche cette aide");
+	app.add_command("close", _close, "Ferme la connexion avec la socket");
+	app.add_command("load", _load, "Charge une définition ou une configuration\n\
+\tUsage: load (conf[figuration] | def[finition] \"FILE_OR_NAME\"\n\
+\t\tFILE_OR_NAME: fichier de configuration (chemin) ou nom du pad");
+	app.add_command("pad", _pad, "Commande de pad.\n"
+								 "\tUsage: pad [ID] (new \"NAME\"|load \"FILE\"|input \"ARGS\"|list|remmove|select ID)\n"
+								 "\t\tID: Préciser l'id du pad pour le sélectionner,  la commande suivante s'appliquera sur ce pad\n"
+								 "\t\tnew \"NAME\": Charge et selectionne en pad actif le pad NAME. Si le pad est déja chargé, il ser simplement sélectionné\n"
+								 "\t\tload \"FILE\": Charger la configuration du pad FILE. Si le pad de la configuration n'est pas chargé, il le sera automatiquement "
+								 "sinon il écrasera la configuration précédente.\n"
+								 "\t\tinput \"ARGS\": Simule une entrée du pad. ARGS doit être un Json de MidiMessage ou MidiMEssageList (exemple:"
+								 "[\"noteon\", 1, 54,127] ou [[\"noteon\", 1, 54,127][\"noteon\", 1, 56,127]])\n"
+								 "\t\tremove: supprime le pad actif (ou celui explicitement cité)\n"
+								 "\t\select ID: Change le pad actif par ID\n"
+								 "\t\tlist: Liste les différents pad\n");
+	app.add_command("exit", _exit, "Ferme l'application");
+}

+ 261 - 0
src/utils.cpp

@@ -0,0 +1,261 @@
+#include "utils.h"
+#include <fstream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+std::string str(int x, const char* format)
+{
+    char tmp[128];
+    sprintf(tmp, format, x);
+    return tmp;
+}
+
+
+std::string str(const MidiRawMessage& x)
+{
+    std::string out = "[";
+    int size = x.size();
+    for(int i=0; i<size; i++)
+        out+=(i>0?" ":"")+str((int)x[i], "%2X");
+    out+="]";
+    return out;
+}
+
+template<typename T>
+class RingBuffer
+{
+    public:
+        RingBuffer(int n){
+            m_data.resize(n);
+            m_size=n;
+            m_head=0;
+            m_tail=-1;
+        }
+        virtual ~RingBuffer(){}
+
+        void enqueue(const T& val){
+            if( m_head==m_tail ) throw "Ring Buffer Overflow";
+            m_data[m_head]=val;
+            m_head=(m_head+1)%m_size;
+        }
+
+        T& dequeue(){
+            if( ((m_tail+1)%m_size) != m_head)
+            {
+                m_tail=(m_tail+1)%m_size;
+                return m_data[m_tail];
+            }
+            throw "Empty Ring Buffer";
+        }
+
+        T& peak() const
+        {
+            if( ((m_tail+1)%m_size) != m_head)
+            {
+                return m_data[(m_tail+1)%m_size];
+            }
+            throw "Empty Ring Buffer";
+        }
+
+        int size() const
+        {
+            if(m_head>m_tail)
+            {
+                return m_head-m_tail-1;
+            }
+            else{
+                return m_size-m_tail-1+m_head;
+            }
+        }
+
+    protected:
+        int             m_size;
+        int             m_head;
+        int             m_tail;
+        std::vector<T>  m_data;
+};
+
+
+bool start_with(const std::string& s1, const std::string& s2, bool ignoreCase)
+{
+    int ss1 = s1.size(), ss2=s2.size();
+    int min=(ss1>ss2)?ss2:ss1;
+    for(int i=0; i<min; i++){
+        char c1 = s1[i], c2 = s2[i];
+        if(ignoreCase){
+            c1=tolower(c1);
+            c2=tolower(c2);
+        }
+        if(c1!=c2) return false;
+    }
+    return true;
+}
+
+Json::Value json_parse_file(const std::string& filename)
+{
+    Json::Value root;   // will contains the root value after parsing.
+    std::ifstream file(filename);
+    if(file.is_open())
+    {
+        file >> root;
+        file.close();
+        return root;
+    }
+    return Json::Value();
+}
+
+typedef int (*args_fucntion)(Args*, char** argv);
+
+int set_config_file(Args* arg, char** argv)
+{
+    arg->config_file=*argv;
+    return 1;
+}
+
+int print_help(Args* arg, char** argv);
+
+int _override_app_dir(Args* arg, char** argv){
+    arg->override_app_dir=*argv;
+    return 1;
+}
+
+int _override_pad_dir(Args* arg, char** argv){
+    arg->override_pad_dir=*argv;
+    return 1;
+}
+
+
+
+int _input_command(Args* args, char** argv)
+{
+    args->use_stdin=false;
+    args->use_pipe=false;
+    args->use_socket=false;
+	std::string arg = std::string(*argv);
+    if(arg=="stdin")
+    {
+        args->use_stdin=true;
+        return 1;
+    }
+	else if(arg=="inet")
+    {
+		args->use_socket=true;
+        args->use_socket_port=atoi(*(argv+1));
+        return 2;
+    }
+	else if(arg=="unix")
+    {
+        args->use_pipe=true;
+        args->use_pipe_filename=*(argv+1);
+        return 2;
+    }
+    else{
+        print_help(args, argv);
+        exit(-1);
+    }
+}
+
+
+int _keep_alive(Args* args, char** argv)
+{
+	args->keep_alive=1;
+	return 0;
+}
+
+int _one_command(Args* args, char** argv)
+{
+	args->keep_alive=2;
+	return 0;
+}
+
+const void* ARGS_FUNCTION_LIST[][5]={
+    /* {LONG_OPTION; SHORT_OPTION, handler, param string for help, long help}*/
+    {"--config", "-c", (void*)set_config_file, "CONFIG_FILE", "Utilise le fichier de configuration CONFIG_FILE"},
+    {"--app-dir", "-d", (void*)_override_app_dir, "APP_DIR", "Change le dossier (remplace la config) de l'application avec APP_DIR"},
+	{"--pads-dir", "-P", (void*)_override_pad_dir, "PAD_DIR", "Change le dossier (remplace la config) des pas avec PAD_DIR"},
+	{"--keep-alive", "-k", (void*)_keep_alive, "", "Garde la connexion avec la socket ouverte après une commande"},
+	{"--one-command", "-o", (void*)_one_command, "", "Ferme le socket après chaque commande"},
+	{"--command", "-C", (void*)_input_command, "stdin | unix FILENAME | inet PORT", "La manière de lire les commandes. stdin: entrée standard (par défaut) \
+, pipe FILENAME : va créer une pipe FILENAME, socket PORT: va lire sur lor port PORT"},
+    {"--help", "-h", (void*)print_help, "", "Affiche cette aide"}
+} ;
+
+
+
+int print_help(Args* arg, char** argv)
+{
+    std::cerr << "simplemidi [OPTIONS] PAD_CONFIGURATION_FILE " << "\n";
+    int l = sizeof (ARGS_FUNCTION_LIST)/sizeof (*ARGS_FUNCTION_LIST);
+    std::cerr << "OPTIONS:\n";
+    for(int i=0; i<l; i++)
+    {
+        std::cerr << "\t" << (const char*)ARGS_FUNCTION_LIST[i][0];
+        if((const char*)ARGS_FUNCTION_LIST[i][1]) std::cerr << "|" << (const char*)ARGS_FUNCTION_LIST[i][1];
+        std::cerr << " " <<(const char*)ARGS_FUNCTION_LIST[i][3] << " : \n\t\t" << (const char*)ARGS_FUNCTION_LIST[i][4] << "\n";
+    }
+    arg->continue_app=false;
+    return 0;
+}
+
+Args::Args(int argc, char** argv)
+{
+    continue_app=true;
+	keep_alive=0;
+    use_stdin=true;
+    use_pipe=use_socket=false;
+    use_pipe_filename="";
+    use_socket_port=-1;
+    for(int i=1; i<argc; i++)
+    {
+        std::string arg = argv[i];
+        int j, l = sizeof (ARGS_FUNCTION_LIST)/sizeof (*ARGS_FUNCTION_LIST);
+        for(j=0; j<l; j++)
+        {
+            if(arg==(const char*)ARGS_FUNCTION_LIST[j][0] || arg==(const char*)ARGS_FUNCTION_LIST[j][1])
+			{
+                i+=((args_fucntion)ARGS_FUNCTION_LIST[j][2])(this, argv+i+1);
+                break;
+            }
+        }
+        if(j==l){
+            pad_file=argv[i];
+            return;
+        }
+    }
+    if(pad_file==""){
+        print_help(this, NULL);
+    }
+}
+
+bool Fs::exists(const std::string& path)
+{
+    return is_file_exists(path, (~S_IFDIR)&0xffff);
+}
+
+bool Fs::is_file(const std::string& path)
+{
+    return is_file_exists(path, (~S_IFDIR)&0xff00);
+}
+
+bool Fs::is_dir(const std::string& path)
+{
+    return is_file_exists(path, S_IFDIR);
+}
+
+bool Fs::is_file_exists(const std::string& path, int mode)
+{
+    struct stat s;
+    if(stat(path.c_str(), &s))
+    {
+        return false;
+    }
+    return s.st_mode&mode;
+}
+
+bool Fs::remove(const std::string& path)
+{
+    if(is_file(path))
+        return !::remove(path.c_str());
+    return false;
+}

+ 139 - 0
ui/GtkButtonGrid.py

@@ -0,0 +1,139 @@
+import math
+
+import gi
+from gi.repository import Gdk
+
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+def rad(n): return n/360*(2*3.1415)
+
+class ButtonWrapper(Gtk.Button):
+    def __init__(self, *args, **kwds):
+        super().__init__(*args, **kwds)
+        self.connect("clicked", self.on_click)
+        self.select()
+        self.sel=None
+        self.definition=None
+        self.color=0
+        self.index=0
+        self.colors=[(0x20/0xff,0x20/0xff,0x20/0xff), (0x20/0xff,0xE0/0xff,0xE0/0xff)]
+        self.is_selected=False
+        self._btn_selected=None
+        self._btn_unselected=None
+        self.show=True
+        self.show_all()
+
+    def on_click(self, x):
+        if self.is_selected:
+            if self._btn_unselected: self._btn_unselected(self.index, self)
+        else:
+            if self._btn_selected: self._btn_selected(self.index, self)
+
+    def do_draw(self, cr):
+        if not self.show: return
+        allocation = self.get_allocation()
+        fg_color = self.get_style_context().get_color(Gtk.StateFlags.NORMAL)
+        cr.set_source_rgba(*self.colors[0]);
+        cr.rectangle(4,4,allocation.width-8, allocation.height-8)
+        cr.stroke()
+        cr.set_source_rgba(*self.colors[self.color]);
+        cr.rectangle(6,6,allocation.width-12, allocation.height-12)
+        cr.fill()
+
+    def select(self):
+        self.is_selected=True
+        self.color=1
+
+
+
+    def unselect(self):
+        self.is_selected=False
+        self.color=0
+
+    @staticmethod
+    def new(sel, mat, index):
+        btn = ButtonWrapper()
+        btn.sel=sel
+        btn.definition=mat
+        btn.index=index
+        return btn
+
+
+class GtkButtonGrid(Gtk.Grid):
+
+    def __init__(self, *args, **kwds):
+        super().__init__(*args, **kwds)
+        self.width=0
+        self.height=0
+        self.map=[]
+        self.x=0
+        self.y=0
+        self.sel=None
+        self.type=None
+        self._btn_selected=None
+        self._btn_unselected=None
+        self.set_hexpand(True)
+        self.set_vexpand(True)
+        self.set_row_homogeneous(True)
+        self.set_column_homogeneous(True)
+
+    def set_on_select(self, b): self._btn_selected=b
+    def set_on_unselect(self, b): self._btn_unselected=b
+
+    def unselect_all(self):
+        for k in self.map:
+            k.unselect()
+
+    def new(self, x, y, sel, t):
+        btn = GtkButtonGrid()
+        btn.x=x
+        btn.y=y
+        btn.sel=sel
+        btn.type=type
+        return btn
+
+    def update(self):
+        self.queue_draw()
+
+    def select(self, i, j=-1):
+        if j>=0: i+=j*self.width
+        if self.map[i]: self.map[i].select()
+
+    def unselect(self, i, j=-1):
+        if j>=0: i+=j*self.width
+        if self.map[i]: self.map[i].unselect()
+
+    def set_grid_size(self, x, y):
+        for i in range(self.width): self.remove_column(0)
+        for i in range(self.height): self.remove_row(0)
+        self.width=x
+        self.height=y
+        self.map=[]
+        for i in range(x*y): self.map.append(None)
+        for i in range(self.width): self.insert_column(0)
+        for i in range(self.height): self.insert_row(0)
+        self.show_all()
+
+    def __getitem__(self, item):
+        if isinstance(item, (tuple, list)): item=item[0]+item[1]*self.width
+        return self.map[item]
+
+    def __setitem__(self, key, value):
+        if isinstance(key, (tuple, list)): key=key[0]+key[1]*self.width
+        x=key%self.width
+        y=math.floor(key/self.width)
+        if(value[1]):
+            self.map[key]=ButtonWrapper.new(value[0], value[1], value[2])
+            self.map[key]._btn_selected=self._btn_selected
+            self.map[key]._btn_unselected=self._btn_unselected
+            self.attach(self.map[key], x,y,1,1)
+        else:
+            self.map[key]=ButtonWrapper.new(value[0], value[1], value[2])
+            self.map[key].show=False
+            self.attach(self.map[key], x,y,1,1)
+
+        return self.map[key]
+
+
+

+ 53 - 0
ui/GtkSelectionList.py

@@ -0,0 +1,53 @@
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+
+class GtkSelectionList(Gtk.ListBox):
+
+    def __init__(self, *args, **kwds):
+        super().__init__(*args, **kwds)
+        self.connect("row-selected", self.on_selection)
+        self.selections={}
+        self._on_selected=None
+        self.current_selection_name=""
+
+    def init(self):
+        ch=self.get_children()
+        for c in ch: super().remove(c)
+        self.current_selection_name=""
+
+    def set_on_selected(self, a):
+        self._on_selected=a
+
+    def rename(self, old, new):
+        self.selections[new]=self.selections[old]
+        del self.selections[old]
+        self.selections[new][0].set_text(new)
+
+    def append(self, name, sel):
+        label = Gtk.Label.new(name)
+        label.set_alignment(0,0)
+        self.add(label)
+        label.show_all()
+        self.selections[name]=(label, sel)
+
+    def remove(self, name):
+        if name in self.selections:
+            lbl = self.selections[name][0]
+            for i in range(len(self.selections)):
+                row = self.get_row_at_index(i)
+                label = row.get_child()
+                if label==lbl:
+                    super().remove(row)
+            del self.selections[name]
+
+    def on_selection(self, obj, c):
+        for k in self.selections:
+            if c.get_child()==self.selections[k][0]:
+                self.current_selection_name=k
+                self._on_selected(k, self.selections[k][1])
+
+
+

+ 135 - 0
ui/MidiConst.py

@@ -0,0 +1,135 @@
+
+ARR_NOTE=["C","C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
+ARR_NOTE_LATIN=["Do","Do#", "Ré", "Ré#", "Mi", "Fa", "Fa#", "Sol", "Sol#", "La", "La#", "Si"]
+
+
+NOTES={
+    "C" : 0,
+    "C#" : 1,
+    "Db" : 1,
+    "D" : 2,
+    "D#" : 3,
+    "Eb" : 3,
+    "E" : 4,
+    "F" : 5,
+    "F#" : 6,
+    "Gb" : 6,
+    "G" : 7,
+    "G#" : 8,
+    "Ab" : 8,
+    "A" : 9,
+    "A#" : 10,
+    "Bb" : 10,
+    "B" : 11
+}
+NOTES_LATIN={
+    "Do" : 0,
+    "Do#" : 1,
+    "Réb" : 1,
+    "Ré" : 2,
+    "Ré#" : 3,
+    "Mib" : 3,
+    "Mi" : 4,
+    "Fa" : 5,
+    "Fa#" : 6,
+    "Solb" : 6,
+    "Sol" : 7,
+    "Sol#" : 8,
+    "Lab" : 8,
+    "La" : 9,
+    "La#" : 10,
+    "Sib" : 10,
+    "Si" : 11
+}
+__C0_START=12
+
+class NoteAngloSaxon:
+    C = 0
+    Cd = 1
+    Db = 1
+    D = 2
+    Dd = 3
+    Eb = 3
+    E = 4
+    F = 5
+    Fd = 6
+    Gb = 6
+    G = 7
+    Gd = 8
+    Ab = 8
+    A = 9
+    Ad = 10
+    Bb = 10
+    B = 11
+
+class Note:
+    @staticmethod
+    def _parse(s, system):
+        octave=4
+        note=""
+        i=0
+        l=len(s)
+        while i<l and s[i] in ("abcdefgABCDEFG#"):
+            note+=s[i]
+            i+=1
+        while i<l and not s[i] in ("0123456789"): i+=1
+        octave=int(s[i])
+        note=note[0].upper()+note[1:]
+        return 12+12*octave+system[note]
+
+    @staticmethod
+    def parse_latin(s):
+        return Note._parse(s, NOTES_LATIN)
+
+    @staticmethod
+    def parse(s):
+        return Note._parse(s, NOTES)
+
+    @staticmethod
+    def to_str(n):
+        n-=12
+        return ARR_NOTE[n%12]+str(int(n/12))
+
+    @staticmethod
+    def to_str_latin(n):
+        n-=12
+        return ARR_NOTE_LATIN[n%12]+str(int(n/12))
+
+    @staticmethod
+    def get_octave(n):
+        n-=12
+        return int(n/12)
+
+    @staticmethod
+    def get_note_str(n):
+        n-=12
+        return ARR_NOTE[n%12]
+
+    @staticmethod
+    def get_note(n):
+        n-=12
+        return n%12
+
+
+class Gamme:
+
+    def __init__(self, range, length=12):
+        self.range=range
+        self.length=length
+
+MAJEUR=Gamme([])
+
+
+GAMME_CHROMATIQUE=[0,1,2,3,4,5,6,7,8,9,10,11]
+GAMME_MAJEUR=[NoteAngloSaxon.C, NoteAngloSaxon.D, NoteAngloSaxon.E, NoteAngloSaxon.F, NoteAngloSaxon.G,
+             NoteAngloSaxon.A, NoteAngloSaxon.B]
+
+GAMME_MINEUR_NATURELLE=[NoteAngloSaxon.C, NoteAngloSaxon.D, NoteAngloSaxon.Eb, NoteAngloSaxon.F, NoteAngloSaxon.G,
+             NoteAngloSaxon.Ab, NoteAngloSaxon.Bb]
+
+GAMME_MINEUR_HARMONIQUE=[NoteAngloSaxon.C, NoteAngloSaxon.D, NoteAngloSaxon.Eb, NoteAngloSaxon.F, NoteAngloSaxon.G,
+             NoteAngloSaxon.Ab, NoteAngloSaxon.B]
+
+GAMME_MINEUR_MELODIQUE=[NoteAngloSaxon.C, NoteAngloSaxon.D, NoteAngloSaxon.Eb, NoteAngloSaxon.F, NoteAngloSaxon.G,
+             NoteAngloSaxon.Ab, NoteAngloSaxon.B]
+

+ 19 - 0
ui/OperationBox.py

@@ -0,0 +1,19 @@
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+
+class OperationBox(Gtk.VBox):
+
+    def __init__(self, *args, **kwds):
+        super().__init__(*args, **kwds)
+        self.current_selection=None
+        self.init(None)
+
+    def init(self, sel):
+        self.current_selection=sel
+
+        if sel: pass
+        else: pass
+

+ 686 - 0
ui/application.py

@@ -0,0 +1,686 @@
+from GtkSelectionList import GtkSelectionList
+from GtkButtonGrid import GtkButtonGrid
+from gi.repository import Gdk
+from gi.repository import Gtk
+from pad import *
+import os
+import simplemidi
+import sys
+gtk=Gtk
+from MidiConst import *
+def when_button_is_clicked(label):
+    '''
+    Quand le bouton est cliqué
+    '''
+    label.set_text('Hello world!')
+
+
+def ok(btn):
+    print("OK clicked")
+
+_GAMME = {
+    "Personalisée": "",
+    "Majeur" : GAMME_MAJEUR,
+    "Mineur naturelle" : GAMME_MINEUR_NATURELLE,
+    "Mineur harmonique" : GAMME_MINEUR_HARMONIQUE,
+    "Mineur mélodique" : GAMME_MINEUR_MELODIQUE
+}
+
+
+
+class Pane:
+
+    def __init__(self, app):
+        self.app = app
+        self.pad=app.pad
+        self.builder = app.builder
+
+    def connect(self, name, signal, fct):
+        self.builder.get_object(name).connect(signal, fct)
+
+    def widget(self, name):
+        return self.builder.get_object(name)
+
+class _Dialog:
+    def __init__(self, parent, name):
+        self.builder=parent.builder
+        self.parent=parent
+        self.dialog=self.widget(name)
+        self.dialog.connect("delete-event", self.drop)
+        self.modify=False
+        self.isInit=False
+
+
+    def on_ok(self, btn):
+        self.dialog.hide()
+        self.dialog.hide()
+        if self.modify:
+            self.parent.on_modify_return(self.get_data())
+        else:
+            self.parent.on_add_return(self.get_data())
+
+    def on_cancel(self, x=None):
+        self.dialog.hide()
+        self.dialog.hide()
+
+    def run(self):
+        if not self.isInit:
+            self.dialog.show()
+            self.isInit=True
+            self.dialog.hide()
+        self.update(None)
+        self.dialog.show()
+
+    def get_data(self):
+        raise NotImplementedError
+
+    def _init(self, data, ):
+        raise NotImplementedError
+
+    def drop(self,a,b):
+        self.dialog.hide()
+        return True
+
+    def connect(self, name, signal, fct):
+        self.builder.get_object(name).connect(signal, fct)
+
+    def widget(self, name):
+        return self.builder.get_object(name)
+
+    def init(self, data):
+        self.modify=data!=None
+        self._init(data)
+        self.update(None)
+
+class NewDialog(_Dialog):
+
+    def __init__(self, pane):
+        super().__init__(pane, "new_pad_conf_dialog")
+        self.cb=self.widget("cb_new_pad")
+        l=PadManager.pad_list()
+        for k in l: self.cb.append(k,k)
+        if len(l): self.cb.set_active(0)
+
+        self.widget("new_pad").connect("clicked", self.on_valid)
+        self.app=pane
+
+    def on_valid(self, x):
+        self.app.on_new(self.cb.get_active_id())
+        self.drop(None,None)
+
+    def update(self, x): pass
+
+class InitDialog(_Dialog):
+
+    def __init__(self, pane):
+        super().__init__(pane, "init_dialog")
+        self.wchannel=[self.widget("init_enable_channel"),self.widget("init_channel_value"),self.widget("init_channel_absolute")]
+        self.wkey=[self.widget("init_enable_key"),self.widget("init_key_value"),self.widget("init_key_absolute")]
+        self.wvelocity=[self.widget("init_enable_velocity"),self.widget("init_velocity_value"),self.widget("init_velocity_absolute")]
+        self.widget("init_ok").connect("clicked", self.on_ok)
+        self.widget("init_cancel").connect("clicked", self.on_cancel)
+        self.wchannel[0].connect("toggled", self.update)
+        self.wkey[0].connect("toggled", self.update)
+        self.wvelocity[0].connect("toggled", self.update)
+        self.message=self.widget("init_message")
+
+    def get_data(self):
+        return [
+            self.message.get_active_id(),
+            int(self.wchannel[1].get_value()) if self.wchannel[0].get_active() else -1,
+            int(self.wkey[1].get_value()) if self.wkey[0].get_active() else -1,
+            int(self.wvelocity[1].get_value()) if self.wvelocity[0].get_active() else -1
+        ]
+
+    def _init(self, data):
+        self.message.set_active(0)
+        self.wchannel[0].set_active(False)
+        self.wkey[0].set_active(False)
+        self.wvelocity[0].set_active(False)
+        self.wchannel[1].set_sensitive(False)
+        self.wkey[1].set_sensitive(False)
+        self.wvelocity[1].set_sensitive(False)
+        self.wchannel[1].set_value(0)
+        self.wkey[1].set_value(0)
+        self.wvelocity[1].set_value(0)
+
+        if data:
+            self.message.set_active_id(data[0])
+            self.wchannel[0].set_active(data[1]>=0)
+            self.wkey[0].set_active(data[2]>=0)
+            self.wvelocity[0].set_active(data[3]>=0)
+            self.wchannel[1].set_sensitive(data[1]>=0)
+            self.wkey[1].set_sensitive(data[2]>=0)
+            self.wvelocity[1].set_sensitive(data[3]>=0)
+
+            self.wchannel[1].set_value(data[1])
+            self.wkey[1].set_value(data[2])
+            self.wvelocity[1].set_value(data[3])
+
+
+    def update(self, x):
+
+        self.wchannel[1].set_sensitive(self.wchannel[0].get_active())
+        self.wkey[1].set_sensitive(self.wkey[0].get_active())
+        self.wvelocity[1].set_sensitive(self.wvelocity[0].get_active())
+
+
+class OperationDialog(_Dialog):
+    def __init__(self, pane):
+        super().__init__(pane, "operation_dialog")
+        self.cbtype=self.widget("operation_type")
+        self.cbtype.connect("changed", self.update)
+        self.current_type="TRANSLATE"
+        self.widget("operation_ok").connect("clicked", self.on_ok)
+        self.widget("operation_cancel").connect("clicked", self.on_cancel)
+        self.frames={
+            "TRANSLATE" : self.widget("operation_translate"),
+            "RANGE" : self.widget("operation_range"),
+            "REORDER" : self.widget("operation_reorder"),
+            "DROP" : self.widget("operation_drop")
+        }
+        self.reorder_offest=self.widget("operation_offset")
+        self.gamme_note=self.widget("operation_gamme_note")
+        self.gamme_gamme=self.widget("operation_gamme_gamme")
+        self.gamme_custom=self.widget("operation_gamme_custom")
+        self.gamme_octave=self.widget("operation_gamme_note_octave")
+
+        self.trans_ch=[self.widget("operation_translate_channel"), self.widget("operation_translate_channel_absolute")]
+        self.trans_key=[self.widget("operation_translate_key"), self.widget("operation_translate_key_absolute")]
+        self.trans_vel=[self.widget("operation_translate_velocity"), self.widget("operation_translate_velocity_absolute")]
+
+        for k in _GAMME: self.gamme_gamme.append(k, k)
+        for k in ARR_NOTE: self.gamme_note.append(k, k)
+
+        self.update()
+
+
+    def update(self, x=None):
+        self.current_type=self.cbtype.get_active_id()
+        for k in self.frames:
+            if k==self.current_type:
+                self.frames[k].show()
+            else:
+                self.frames[k].hide()
+
+    def get_data(self):
+        if self.current_type=="TRANSLATE":
+            values=[self.trans_ch[0].get_value(),self.trans_key[0].get_value(),self.trans_vel[0].get_value()]
+            modes=[self.trans_ch[1].get_active(),self.trans_key[1].get_active(),self.trans_vel[1].get_active()]
+            return TranslateOperation.new(values, modes)
+        elif self.current_type=="DROP":
+            return DropOperation.new()
+        elif self.current_type=="RANGE":
+            gamme=self.gamme_gamme.get_active_id()
+            gamme=_GAMME[gamme] if gamme else json.loads("["+self.gamme_custom.get_text()+"]")
+            note=Note.parse(self.gamme_note.get_active_id()+self.gamme_octave.get_active_id())
+            return RangeOperation.new(gamme, note)
+        elif self.current_type=="REORDER":
+            return ReorderOperation.new(int(self.reorder_offest.get_value()))
+
+    def _init(self, data):
+        self.gamme_gamme.set_active(0)
+        self.gamme_note.set_active(0)
+        self.gamme_octave.set_active(5)
+        self.gamme_custom.set_text("")
+
+        self.trans_ch[0].set_value(0)
+        self.trans_key[0].set_value(0)
+        self.trans_vel[0].set_value(0)
+        self.trans_ch[1].set_active(False)
+        self.trans_key[1].set_active(False)
+        self.trans_vel[1].set_active(False)
+
+        self.reorder_offest.set_value(0)
+
+        if data:
+            self.cbtype.set_active_id(data.type)
+            if isinstance(data, RangeOperation):
+                self.gamme_note.set_active(Note.get_note(data.key))
+                self.gamme_octave.set_active(Note.get_octave(data.key))
+                range_set=False
+                for i in _GAMME:
+                    if _GAMME[i]!="" and _GAMME[i]==data.range:
+                        range_set=True
+                        self.gamme_gamme.set_active_id(i)
+                if not range_set:
+                    self.gamme_custom.set_text(str(data.range)[1:-1])
+
+            if isinstance(data, ReorderOperation):
+                self.reorder_offest.set_value(int(data.offset))
+
+            if isinstance(data, TranslateOperation):
+                self.trans_ch[0].set_value(data.channel)
+                self.trans_key[0].set_value(data.key)
+                self.trans_vel[0].set_value(data.velocity)
+                self.trans_ch[1].set_active(data.channelmode=="FIXED")
+                self.trans_key[1].set_active(data.keymode=="FIXED")
+                self.trans_vel[1].set_active(data.velocitymode=="FIXED")
+
+
+
+
+
+
+
+class CRUDPane(Pane):
+    def __init__(self, app, prefix):
+        super().__init__(app)
+        #self.widget(prefix+"_down").connect("clicked", self.on_down)
+        self.widget(prefix+"_up").connect("clicked", self._on_up)
+        self.widget(prefix+"_remove").connect("clicked", self._on_remove)
+        self.widget(prefix+"_add").connect("clicked", self._on_add)
+        self.widget(prefix+"_edit").connect("clicked", self._on_edit)
+        self.list=self.widget(prefix+"_listbox")
+        self.dialog=None
+        self.data=[]
+
+    def print(self, tmp="After"):
+        pass
+
+    def get_index(self): return self.find_index(self.list.get_selected_row())
+
+    def find_index(self, widget):
+        n=-1
+        if not widget: return -1
+        widget=widget.get_child()
+        for i in range(len(self.data)):
+            k=self.data[i]
+            if k[1]==widget:
+                return i
+        return -1
+
+    def insert(self, i, data):
+        self.data.append(None)
+        for j in reversed(range(i,len(self.data)-1)):
+            self.data[j+1]=self.data[j]
+        lbl=Gtk.Label.new(str(data))
+        self.data[j] =(data, lbl)
+        self.list.insert(lbl, i)
+        self.list.show_all()
+
+    def _on_up(self, btn):
+        if not self.app.sel_pane.get_current_selection(): return
+        n=self.get_index()
+        if n>0:
+            x=self.data[n]
+            self.list.remove(x[1].get_parent())
+            self.insert(n-1, x[0])
+            self.data.remove(x)
+            self.list.select_row(self.data[n-1][1].get_parent())
+            self.on_up(n)
+            self.print("up")
+
+    def _on_down(self, btn):
+        if not self.app.sel_pane.get_current_selection(): return
+        n=self.get_index()
+        if n>=0 and n<=len(self.data)-1:
+            x=self.data[n]
+            self.list.remove(x[1].get_parent())
+            self.insert(n+1, x[0])
+            self.data.remove(x)
+            self.list.select_row(self.data[n+1][1].get_parent())
+            self.on_down(n)
+            self.print("down")
+
+    def _on_remove(self, btn):
+        if not self.app.sel_pane.get_current_selection(): return
+        n=self.get_index()
+        if n>=0:
+            x=self.data[n]
+            self.list.remove(x[1].get_parent())
+            self.data.remove(x)
+            self.on_remove(n)
+            self.print("remove")
+
+    def _on_add(self, btn):
+        if not self.app.sel_pane.get_current_selection(): return
+        self.dialog.init(None)
+        self.dialog.run()
+
+    def _on_edit(self, btn):
+        if not self.app.sel_pane.get_current_selection(): return
+        n=self.get_index()
+        if n>=0:
+            self.dialog.init(self.data[n][0])
+            self.dialog.run()
+
+    def init(self, data):
+        self.data=[]
+        ch = self.list.get_children()
+        for i in ch: self.list.remove(i)
+        for d in data:
+            self.add_on_ui(d)
+
+    def data_to_str(self, x):
+        return str(x)
+
+    def add_on_ui(self, data):
+        lbl=Gtk.Label.new(self.data_to_str(data))
+        lbl.set_alignment(0,0)
+        self.data.append((data, lbl))
+        self.list.add(lbl)
+        self.list.show_all()
+
+    def on_add_return(self, data):
+        self.add_on_ui(data)
+        self.on_add(data)
+        self.print("add")
+
+
+    def on_modify_return(self, data):
+        n=self.find_index(self.list.get_selected_row())
+        lbl=self.data[n][1]
+        lbl.set_label(self.data_to_str(data))
+        self.data[n]=(data, lbl)
+        self.on_modify(n, data)
+        self.print("modify")
+
+    def on_add(self, x): raise NotImplementedError
+    def on_modify(self, i, x): raise NotImplementedError
+    def on_remove(self, i): raise NotImplementedError
+    def on_up(self, i): raise NotImplementedError
+    def on_down(self, i): raise NotImplementedError
+
+class InitPane(CRUDPane):
+    def __init__(self, app):
+        super().__init__(app, "init")
+        self.dialog=InitDialog(self)
+
+    def data_to_str(self, x):
+        out=""
+        if x[0]=="noteon": out="NoteOn("
+        if x[0]=="noteoff": out="NoteOff("
+        args=[]
+        if x[1]>=0: args.append("channel %d"%x[1])
+        if x[2]>=0: args.append("key %d"%x[2])
+        if x[3]>=0: args.append("velocity %d"%x[3])
+        return out+",".join(args)+")"
+
+    def on_add(self, x):
+        sel = self.app.sel_pane.get_current_selection()
+        sel.init.actions.append(InputAction(x))
+
+    def on_modify(self, i, x):
+        sel = self.app.sel_pane.get_current_selection()
+        sel.init.actions[i]=InputAction(x)
+
+    def on_remove(self, i):
+        sel = self.app.sel_pane.get_current_selection()
+        sel.init.actions.remove(sel.init.actions[i])
+
+
+    def on_up(self, i):
+        sel = self.app.sel_pane.get_current_selection()
+        tmp=sel.init.actions[i]
+        sel.init.actions[i]=sel.init.actions[i-1]
+        sel.init.actions[i-1]=tmp
+
+    def on_down(self, i):
+        sel = self.app.sel_pane.get_current_selection()
+        tmp=sel.init.actions[i]
+        sel.init.actions[i]=sel.init.actions[i+1]
+        sel.init.actions[i+1]=tmp
+
+
+class OperationPane(CRUDPane):
+    def __init__(self, app):
+        super().__init__(app, "operation")
+        self.dialog=OperationDialog(self)
+
+    def on_add(self, x):
+        sel = self.app.sel_pane.get_current_selection()
+        sel.operations.append(x)
+
+    def on_modify(self, i, x):
+        sel = self.app.sel_pane.get_current_selection()
+        sel.operations[i]=x
+
+    def on_remove(self, i):
+        sel = self.app.sel_pane.get_current_selection()
+        sel.operations.remove(sel.operations[i])
+
+    def on_up(self, i):
+        sel = self.app.sel_pane.get_current_selection()
+        tmp=sel.operations[i]
+        sel.operations[i]=sel.operations[i-1]
+        sel.operations[i-1]=tmp
+
+    def on_down(self, i):
+        sel = self.app.sel_pane.get_current_selection()
+        tmp=sel.operations[i]
+        sel.operations[i]=sel.operations[i+1]
+        sel.operations[i+1]=tmp
+
+
+
+class SelectionPane(Pane):
+
+    def __init__(self, app):
+        super().__init__(app)
+        self.input_new_pad=self.builder.get_object("input_new_pad")
+        self.sel_list=GtkSelectionList()
+        self.pad=app.pad
+        self.builder.get_object("sel_pane_root").pack_start(self.sel_list, True, True, 0)
+        self.builder.get_object("sel_pane_root").reorder_child(self.sel_list,1)
+        self.sel_list.set_on_selected(self.on_selections_changed)
+        self.connect("button_new_sel", "clicked", self.on_new_sel)
+        self.grid=GtkButtonGrid()
+        self.grid.set_on_select(self.on_button_selected)
+        self.grid.set_on_unselect(self.on_button_unselected)
+        self.widget("grid_root").pack_start(self.grid, True, True, 0)
+        self.entry_name=self.widget("entry_sel_name")
+        self.widget("rename_sel").connect("clicked", self.on_name_change)
+        self.sel_edit_box=self.widget("sel_edit_box")
+        self.modifying=False
+        self.init_pane=InitPane(self.app)
+        self.op_pane=OperationPane(self.app)
+
+    def on_name_change(self, editable):
+        old=self.sel_list.current_selection_name
+        new=self.entry_name.get_text()
+        self.pad.rename_selection(old, new)
+        self.sel_list.rename(old, new)
+
+
+
+    def on_new_sel(self, btn):
+        name = self.input_new_pad.get_text()
+        sel=self.pad.new_sel(name)
+        self.sel_list.append(name, sel)
+        self.input_new_pad.set_text("")
+
+    def on_selections_changed(self, sel, selection):
+        self.set_selection(sel, selection)
+        self.entry_name.set_text(sel if sel else "")
+
+
+    def set_selection(self, name, selection):
+        self.modifying=True
+        self.grid.unselect_all()
+        if name:
+            for i in selection.inputs:
+                self.grid[i].select()
+            self.sel_edit_box.set_visible(True)
+            self.sel_edit_box.show()
+            self.sel_edit_box.queue_draw()
+            self.init_pane.init(selection.init.json())
+            self.op_pane.init(selection.operations)
+            self.sel_edit_box.show()
+        else:
+            self.init_pane.init([])
+            self.op_pane.init([])
+            self.sel_edit_box.hide()
+
+        self.grid.update()
+        self.modifying=False
+
+
+    def init(self, pad):
+        self.modifying=True
+        self.pad=pad
+        self.sel_list.init()
+        print(self.sel_list.get_children())
+        for k in self.pad.selections:
+            self.sel_list.append(k, self.pad.selections[k])
+
+        self.grid.set_grid_size(self.pad.width, self.pad.height)
+        for i in range(self.pad.width*self.pad.height):
+            sel = self.pad.find_selection_of(i)
+            if sel: sel=self.pad.selections[sel]
+            self.grid[i]=(sel, self.pad.matrix[i], i)
+        self.modifying=False
+
+    def get_current_selection_name(self):
+        return self.sel_list.current_selection_name
+
+    def get_current_selection(self):
+        n=self.sel_list.current_selection_name
+        if n and n in self.pad.selections:
+            return self.pad.selections[n]
+        return None
+
+    def on_button_selected(self, i, btn):
+        if not self.modifying and self.get_current_selection_name():
+            self.pad.add_button_to(self.sel_list.current_selection_name, i)
+            btn.select()
+
+    def on_button_unselected(self, i, btn):
+        if not self.modifying and self.get_current_selection_name():
+            self.pad.remove_button_selection(i)
+            btn.unselect()
+
+
+
+
+
+
+class Application:
+
+    def __init__(self):
+        self.builder = Gtk.Builder()
+        self.builder.add_from_file('ui.glade')
+        self.window=self.builder.get_object('main_window')
+        self.window.connect('delete-event', Gtk.main_quit)
+        self.pad=None
+        self.sel_pane=SelectionPane(self)
+        self.window.show_all()
+        self.widget("menu_open").connect("activate", self.open_dialog)
+        self.widget("menu_save").connect("activate", self.on_save_as)
+        self.widget("menu_save_as").connect("activate", self.save_dialog)
+        self.widget("menu_new").connect("activate", self.new_dialog)
+        self.widget("toolbox_start").connect("clicked", self.start_simple_midi)
+        self.widget("toolbox_clear").connect("clicked", self.reset_simple_midi)
+        self.file_open=None
+        self.file_dir_open=None
+        self.changed=False
+        self.simplemidi= simplemidi.SimpleMidi()
+        self.simplemidi.run()
+        self.widget("notebook").hide()
+        self.dialog_new=NewDialog(self)
+
+    def widget(self, name):
+        return self.builder.get_object(name)
+
+    def connect(self, name, signal, fct):
+        self.builder.get_object(name).connect(signal, fct)
+
+    def set_pad_configuration(self, conf):
+        self.pad=PadConfiguration.parse(conf)
+        self.init()
+
+    def set_new_pad(self, pad):
+        self.pad=PadConfiguration.new(pad)
+        self.init()
+
+    def set_changed(self):
+        self.cahnged=True
+        self.window.set_title("*"+os.path.basename(self.file_open))
+
+
+
+    def init(self):
+        self.sel_pane.init(self.pad)
+
+    def open(self, file :str):
+        self.set_pad_configuration(file)
+        self.window.show_all()
+        self.sel_pane.set_selection(None, None)
+        self.file_open=file
+        self.file_dir_open=os.path.dirname(file)
+        self.widget("selection_root_pane").set_position(100)
+        self.window.set_title(os.path.basename(self.file_open))
+        self.simplemidi.set_pad(self.pad.name)
+
+    def save(self, file):
+        with open(file, "w") as f:
+            f.write(json.dumps(self.pad.json(), indent=4))
+        self.changed=False
+
+    def test(self):
+        with open("test", "w") as f:
+            f.write(json.dumps(self.pad.json(), indent=4))
+
+
+    def open_dialog(self, x):
+        dialog = Gtk.FileChooserDialog("Please choose a file", self.window,
+                                       Gtk.FileChooserAction.OPEN,
+                                       (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+                                        Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
+
+
+        response = dialog.run()
+        if response == Gtk.ResponseType.OK:
+            file_path = dialog.get_filename()
+            self.open(file_path)
+        elif response == Gtk.ResponseType.CANCEL:
+            pass
+        dialog.destroy()
+
+    def save_dialog(self, x):
+        dialog = Gtk.FileChooserDialog("Please choose a file", self.window,
+                                       Gtk.FileChooserAction.SAVE,
+                                       (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+                                        Gtk.STOCK_SAVE_AS, Gtk.ResponseType.OK))
+        dialog.set_current_folder(self.file_dir_open if self.file_dir_open  else ".")
+        dialog.set_filename(os.path.basename(self.file_open))
+
+        response = dialog.run()
+        if response == Gtk.ResponseType.OK:
+            file_path = dialog.get_filename()
+            self.save(file_path)
+        elif response == Gtk.ResponseType.CANCEL:
+            pass
+        dialog.destroy()
+
+    def on_save_as(self, x):
+        if self.file_dir_open:
+            self.save(self.file_open)
+        else:
+            self.save_dialog(None)
+    def new_dialog(self, x):
+        self.dialog_new.run()
+
+    def reset_simple_midi(self, x):
+        if self.pad:
+            self.simplemidi.clear()
+
+    def start_simple_midi(self, x):
+        if self.pad:
+            self.simplemidi.set_configuration_data(self.pad)
+
+    def on_new(self, pad):
+        self.set_new_pad(pad)
+        self.file_open=pad+" configuration"
+        self.file_dir_open=None
+        self.set_changed()
+        self.window.show_all()
+
+    def main(self):
+        self.window.show()
+        Gtk.main()
+
+
+app = Application()
+app.open("conf3.conf")
+app.main()

+ 4 - 0
ui/config.py

@@ -0,0 +1,4 @@
+
+
+PAD_DIRECTORY="../pads"
+PAD_ROUTER_BIN="./simplemidi"

+ 54 - 0
ui/main.py

@@ -0,0 +1,54 @@
+from padbuilder import InputDef, Pad
+
+pad = Pad("APC Mini", 10, 12, "APC MINI:APC MINI MIDI", "APC MINI:APC MINI MIDI")
+
+button = InputDef.button("button")
+button.add_noteon("on", -1, -1, 1)
+button.add_noteon("off", -1, -1, 0)
+button.add_noteon("clear", -1, -1, 0)
+button.add_noteon("green", -1, -1, 1)
+button.add_noteon("_blink", -1, -1, 2)
+button.add_noteon("red", -1, -1, 3)
+button.add_noteon("red_blink", -1, -1, 4)
+button.add_noteon("yellow", -1, -1, 5)
+button.add_noteon("yellow_blink", -1, -1, 6)
+pad.add_inputdef(button)
+
+
+circlebutton = InputDef.button("circlebutton")
+circlebutton.add_noteon("on", -1, -1, 1)
+circlebutton.add_noteon("off", -1, -1, 0)
+circlebutton.add_noteon("clear", -1, -1, 0)
+pad.add_inputdef(circlebutton)
+
+controller = InputDef.controller("controller")
+pad.add_inputdef(controller)
+
+i=0
+for y in range(8):
+    for x in range(8):
+        pad[x,7-y]=button.instance(1, i)
+        i+=1
+
+for i in range(8):
+    pad[9, i] = button.instance(1, 82+i)
+
+for i in range(8):
+    pad[i, 9] = button.instance(1, 64+i)
+
+for i in range(9):
+    pad[i, 11] = controller.instance(1, 48+i)
+
+pad[9,9]=button.instance(1,98)
+
+
+
+pad.write("../pads/apc.json")
+
+
+
+
+
+
+
+

+ 313 - 0
ui/pad.py

@@ -0,0 +1,313 @@
+import math
+import json
+from padmanager import PadManager
+
+class InputAction(list):
+
+    def __init__(self, js):
+        super().__init__(js)
+
+    def json(self):
+        return self
+
+
+class InputActionList():
+
+    def __init__(self, js):
+        self.actions=[]
+        if len(js)>0:
+            if isinstance(js[0], str):
+                self.actions.append(InputAction(js))
+            else:
+                for k in js:
+                    self.actions.append(k)
+
+    def json(self):
+        x=[]
+        for k in self.actions:
+            x.append(k)
+        return x
+
+
+class InputDefinition:
+
+    def __init__(self, js):
+        self.type=js["type"]
+        self.actions={}
+        for k in js["actions"]:
+            self.actions[k]=InputActionList(js["actions"][k])
+        self.shape=js["shape"] if "shape" in js else ""
+
+    def json(self):
+        actions={}
+        for k in self.actions:
+            actions[k]=self.actions[k].json()
+        return {
+            "type" : self.type,
+            "actions" : self.actions,
+            "shape" : self.shape
+        }
+
+class Pad:
+
+    def __init__(self, js):
+        self.name=js["name"]
+        self.width=js["width"]
+        self.height=js["height"]
+        self.ports=js["ports"]
+        self.inputs={}
+        for k in js["inputs"]:
+            self.inputs[k]=InputDefinition(js["inputs"][k])
+
+        self.matrix=[]
+        for i in range(self.width*self.height): self.matrix.append(None)
+        for i in range(self.width*self.height):
+            obj=js["matrix"][i]
+            if obj:
+                self.matrix[i]=self.inputs[obj["type"]]
+
+class Operation:
+
+    def __init__(self, type, js=None):
+        pass
+    def json(self): raise NotImplementedError
+
+    @staticmethod
+    def parse(js):
+        cl = CLASSES[js["type"]]
+        return cl(js)
+
+class DropOperation(Operation):
+    TYPE="DROP"
+    def __init__(self, js):
+        super().__init__(js)
+        self.type=DropOperation.TYPE
+    def json(self): return {"type" : DropOperation.TYPE}
+    @staticmethod
+    def new(): return DropOperation(None)
+
+    def __str__(self):
+        return "Drop()"
+
+class ReorderOperation(Operation):
+    TYPE="REORDER"
+    def __init__(self, js):
+        super().__init__(js)
+        self.type=ReorderOperation.TYPE
+        if js:
+            self.offset=js["offset"]
+    def json(self):
+        return {
+            "type" : ReorderOperation.TYPE,
+            "offset" : self.offset
+        }
+    @staticmethod
+    def new(offset):
+        tmp = ReorderOperation(None)
+        tmp.offset=offset
+        return tmp
+
+    def __str__(self):
+        return "Reorder(offset %d)" % self.offset
+
+class TranslateOperation(Operation):
+    RELATIVE="RELATIVE"
+    FIXED="FIXED"
+    UNCHANGED="UNCHANGED"
+    TYPE="TRANSLATE"
+    def __init__(self, js):
+        super().__init__(js)
+        self.type=TranslateOperation.TYPE
+        self.channelmode=TranslateOperation.RELATIVE
+        self.keymode=TranslateOperation.RELATIVE
+        self.velocitymode=TranslateOperation.RELATIVE
+        self.channel=0
+        self.key=0
+        self.velocity=0
+        if js:
+            self.channelmode=js["modes"][0]
+            self.keymode=js["modes"][1]
+            self.velocitymode=js["modes"][2]
+            self.channel=js["values"][0]
+            self.key=js["values"][1]
+            self.velocity=js["values"][2]
+
+    def __str__(self):
+        ch = ("=" if self.channelmode=="FIXED" else ("+" if self.channel>=0 else "")) + str(self.channel)
+        key = ("=" if self.keymode=="FIXED" else ("+" if self.key>=0 else "")) + str(self.key)
+        vel = ("=" if self.velocitymode=="FIXED" else ("+" if self.velocity>=0 else "")) + str(self.velocity)
+        return "Translate(channel %s, key %s, velocity %s)" % (ch, key, vel)
+
+    def json(self):
+        return {
+            "type" : TranslateOperation.TYPE,
+            "values" : [self.channel, self.key, self.velocity],
+            "modes" : [self.channelmode, self.keymode, self.velocitymode]
+        }
+
+    @staticmethod
+    def new(values=[0,0,0], modes=[False,False,False]):
+        _modes=["FIXED" if modes[0] else "RELATIVE","FIXED" if modes[1] else "RELATIVE",
+                "FIXED" if modes[2] else "RELATIVE"]
+        return TranslateOperation({ "values" : values, "modes": _modes})
+
+class RangeOperation(Operation):
+    TYPE="RANGE"
+    def __init__(self, js):
+        super().__init__(js)
+        self.type=RangeOperation.TYPE
+        self.range=[]
+        self.key=0
+        self.length=12
+        if js:
+            self.range=js["range"]
+            self.key=js["key"]
+            self.length=js["length"]
+
+    def json(self):
+        return {
+            "type" : RangeOperation.TYPE,
+            "range" : self.range,
+            "key" : self.key,
+            "length" : self.length
+        }
+
+    def __str__(self):
+        return "RANGE(range %s, key %d, len %d)" % (self.range, self.key, self.length)
+
+    @staticmethod
+    def new(range, key, length=12):
+        tmp = RangeOperation(None)
+        tmp.length=length
+        tmp.range=range
+        tmp.key=key
+        return tmp
+
+CLASSES={
+        TranslateOperation.TYPE : TranslateOperation,
+        DropOperation.TYPE : DropOperation,
+        ReorderOperation.TYPE : ReorderOperation,
+        RangeOperation.TYPE : RangeOperation
+    }
+
+class Selection:
+
+    def __init__(self, pad,  js=None):
+        self.pad=pad
+        self.operations=[]
+        self.locked=[]
+        self.init=InputActionList([])
+        self.inputs=[]
+        if js:
+            for op in js["operations"]:
+                self.operations.append(Operation.parse(op))
+            self.init=InputActionList(js["init"])
+            self.locked=[]
+            for k in js["locked"]:
+                self.locked.append(k[0]+k[1]*pad.width)
+
+    def add_to_sel(self, i, j=0):
+        if j>=0: i+=j*self.pad.width
+        if not i in self.inputs:
+            self.inputs.append(i)
+
+    def remove_from_sel(self, i,j=-1):
+        if j>=0: i+=j*self.pad.width
+        if not i in self.inputs: return
+        out=[]
+        for k in self.inputs:
+            if i!=k:
+                out.append(k)
+        self.inputs=out
+
+    def json(self):
+        ops = []
+        for op in self.operations:
+            ops.append(op.json())
+        lock=[]
+        for i in self.locked:
+            lock.append([i%self.pad.width, math.floor(i/self.pad.width)])
+        return {
+            "operations" : ops,
+            "init" : self.init.json(),
+            "locked" : lock
+        }
+
+
+
+class PadConfiguration(Pad):
+
+    def __init__(self, js, pad=None):
+
+        self.pad=(js["pad"] if js else pad)
+        super().__init__(PadManager.read_pad(self.pad))
+        self.conf_name=""
+        self.description=""
+        self.selections={}
+        if js:
+            self.conf_name = js["name"]
+            self.description=js["description"]
+            for k in js["selections"]:
+                self.selections[k]=Selection(self, js["selections"][k])
+
+            for i in range(len(js["matrix"])):
+                sel = self.selections[js["matrix"][i]] if js["matrix"][i] else None
+                if sel: sel.inputs.append(i)
+
+    def find_selection_of(self, x, y=-1):
+        if y>=0: x+=y*self.width
+        for sel in self.selections:
+            if x in self.selections[sel].inputs: return sel
+        return None
+
+    def new_sel(self, name):
+        sel = Selection(self)
+        self.selections[name]=sel
+        return sel
+
+    def sel(self, name):
+        return self.selections[name]
+
+    def add_button_to(self, selname, i, j=-1):
+        if j>=0: i+=j*self.pad.width
+        for k in self.selections:
+            self.selections[k].remove_from_sel(i)
+        self.selections[selname].add_to_sel(i)
+
+    def remove_button_selection(self, i, j=-1):
+        if j>=0: i+=j*self.pad.width
+        for k in self.selections:
+            self.selections[k].remove_from_sel(i)
+
+    def rename_selection(self, old, new):
+        self.selections[new]=self.selections[old]
+        del self.selections[old]
+
+    def json(self):
+        mat = []
+        for i in range(self.width*self.height):
+            mat.append(self.find_selection_of(i))
+        sels = {
+
+        }
+        for k in self.selections:
+            sels[k]=self.selections[k].json()
+        return {
+            "name" : self.conf_name,
+            "description" : self.description,
+            "pad" : self.pad,
+            "selections" : sels,
+            "matrix" : mat
+        }
+
+    @staticmethod
+    def parse(file):
+        with open(file) as f:
+            return PadConfiguration(json.loads(f.read()))
+
+    @staticmethod
+    def new(pad):
+        return PadConfiguration(None, pad)
+
+
+

+ 152 - 0
ui/pad_config.py

@@ -0,0 +1,152 @@
+import json
+from padbuilder import NoteOn, NoteOff
+
+REL = "RELATIVE"
+FIX = "FIXED"
+
+def Drop(): return { "type": "DROP"}
+
+def Reorder(n=0): return  {
+    "type": "REORDER",
+    "offset" : n
+}
+
+def Translate(ch=0, chm="RELATIVE", k=0, km="RELATIVE", v=0, vm="RELATIVE"):
+    return {
+        "type": "TRANSLATE",
+        "values" : (ch, k, v),
+        "modes" : (chm, km, vm)
+    }
+
+def Range(range, key, l=12):
+    return {
+        "type": "TRANSLATE",
+        "range" : range,
+        "key" : key,
+        "length" : l
+    }
+
+
+
+class Selection:
+
+    def __init__(self, name):
+        self.name = name
+        self.inputs = []
+        self.operations = []
+        self.locked=[]
+        self.init=[]
+
+
+    def __iadd__(self, other):
+        if isinstance(other, list):
+            for x in other: self.inputs.append(x)
+        if isinstance(other, int):
+            self.inputs.append(other)
+
+    def add_input(self, other):
+        if isinstance(other, list):
+            for x in other: self.inputs.append(x)
+        if isinstance(other, int):
+            self.inputs.append(other)
+
+    def add_operation(self, op):
+        self.operations.append(op)
+
+    def add_init_operation(self, op):
+        if isinstance(op, (tuple, list)):
+            for x in op: self.add_init_operation(x)
+        else:
+            self.init.append(op)
+
+    def add_locked(self, op):
+        if isinstance(op, (tuple, list)):
+            for x in op: self.add_locked(x)
+        else:
+            self.locked.append(op)
+
+
+
+    def json(self):
+        return {
+            "operations" : self.operations,
+            "init" : self.init,
+            "locked" : self.locked
+        }
+
+class Configuration:
+
+    def __init__(self, name, pad, w, h):
+        self.w=w
+        self.h=h
+        self.description=""
+        self.name = name
+        self.pad=pad
+        self.selection={}
+
+    def add_selection(self, s): self.selection[s.name] = s
+
+    def json(self):
+        matrix=[]
+        for i in range(self.w*self.h):
+            matrix.append(None)
+        for s in self.selection:
+            sel = self.selection[s]
+            for i in sel.inputs:
+                matrix[i]=sel.name
+        sels = {}
+        for x in self.selection:
+            sels[self.selection[x].name]=(self.selection[x].json())
+
+        return {
+            "name" : self.name,
+            "description" : self.description,
+            "pad" : self.pad,
+            "selections" : sels,
+            "matrix" : matrix
+        }
+
+    def write(self, file):
+        with open(file, "w") as f:
+            f.write(json.dumps(self.json(), indent=4))
+
+
+conf = Configuration("APC Mini", "apc.json", 10, 12 )
+
+
+a = Selection("a")
+a.add_input([0,1,2,3,10,11,12,13,20,21,22,23,30,31,32,33])
+a.add_operation(Translate(1, FIX, 0, REL))
+a.add_init_operation(NoteOn(-1,-1,1))
+conf.add_selection(a)
+
+
+b = Selection("b")
+b.add_input([4,5,6,7,14,15,16,17,24,25,26,27,34,35,36,37])
+b.add_operation(Reorder())
+b.add_operation(Translate(2, FIX, 0, REL))
+b.add_init_operation(NoteOn(-1,-1,3))
+conf.add_selection(b)
+
+
+c = Selection("c")
+c.add_input([44,45,46,47,54,55,56,57,64,65,66,67,74,75,76,77])
+c.add_operation(Translate(3, FIX, 0, REL))
+c.add_init_operation(NoteOn(-1,-1,1))
+conf.add_selection(c)
+
+
+d = Selection("d")
+d.add_input([40,41,42,43,50,51,52,53,60,61,62,63,70,71,72,73])
+d.add_operation(Reorder())
+d.add_operation(Translate(4, FIX, 0, REL))
+d.add_init_operation(NoteOn(-1,-1,3))
+conf.add_selection(d)
+
+e = Selection("e")
+e.add_input([110,111,112,113,114,115,116,117,118])
+e.add_operation(Reorder())
+e.add_operation(Translate(4, FIX, 0, REL))
+conf.add_selection(e)
+
+conf.write("../conf.conf")

+ 122 - 0
ui/padbuilder.py

@@ -0,0 +1,122 @@
+import json
+
+
+class Ports:
+    def __init__(self, js):
+        if isinstance(js, object):
+            self.input = js["input"] if ("input" in js and isinstance(js["input"], str)) else ""
+            self.output = js["output"] if ("output" in js and isinstance(js["output"], str)) else ""
+        else:
+            self.input=""
+            self.output=""
+
+    def json(self): return {"input": self.input, "output" : self.output}
+
+
+def _Note(t, ch=-1, k=-1, vel=-1):
+    return [ t, ch if ch!=None else -1,  k if k!=None else -1, vel if vel!=None else -1]
+
+def NoteOn(ch=-1, k=-1, vel=-1):
+    return _Note("noteon", ch, k, vel)
+
+def NoteOff(ch=-1, k=-1, vel=-1):
+    return _Note("noteoff", ch, k, vel)
+
+
+class InputDef:
+
+    def __init__(self, name, type):
+        self.name=name
+        self.type=type
+        self.actions={}
+
+    @staticmethod
+    def button(name): return InputDef(name, "BUTTON")
+
+    @staticmethod
+    def controller(name): return InputDef(name, "CONTROLLER")
+
+    def add_actions(self, name, action):
+        self.actions[name]=action
+
+    def add_noteon(self, name, ch=-1, k=-1, vel=-1):
+        self.actions[name]=NoteOn(ch, k, vel)
+
+
+    def add_noteoff(self, name, ch=-1, k=-1, vel=-1):
+        self.actions[name]=NoteOn(ch, k, vel)
+
+    def json(self):
+        return {
+            "type" : self.type,
+            "actions" : self.actions
+        }
+
+    def instance(self, ch, key):
+        return {
+            "type" : self.name,
+            "channel" : ch,
+            "key" : key,
+            "velocity" : 0,
+            "locked" : None
+        }
+
+class Pad:
+
+    def __init__(self, name, w, h, ip="", op=""):
+        self.name = name
+        self.width = w
+        self.height = h
+        self.ports = Ports({"input" : ip, "output" : op})
+        self.matrix=[]
+        i=0
+        l = self.width*self.height
+        while i<l:
+            self.matrix.append(None)
+            i+=1
+
+        self.inputdef = {}
+
+
+    def add_inputdef(self, val=None):
+        self.inputdef[val.name]=val
+
+    def set(self, x, y, val):
+        self.matrix[y*self.width+x]=val
+
+    def get(self, x, y):
+        return self.matrix[y*self.width+x]
+
+    def __getitem__(self, item):
+        if isinstance(item, tuple) and len(item) ==2:
+            item=item[0]+item[1]*self.width
+        if isinstance(item, int):
+            return self.matrix[item]
+        else:
+            raise Exception("Pass to Pad[] int or int,int")
+
+    def __setitem__(self, item, val):
+        if isinstance(item, tuple) and len(item) ==2:
+            item=item[0]+item[1]*self.width
+        if isinstance(item, int):
+            self.matrix[item]=val
+        else:
+            raise Exception("Pass to Pad[] int or int,int")
+
+    def json(self):
+        inputs = {}
+        for k in self.inputdef:
+            inputs[k]=self.inputdef[k].json()
+        return {
+            "name" : self.name,
+            "width" : self.width,
+            "height" : self.height,
+            "ports" : self.ports.json(),
+            "inputs" : inputs,
+            "matrix" : self.matrix,
+            "type" : "pad"
+        }
+
+    def write(self, file):
+        with open(file, "w") as f:
+            f.write(json.dumps(self.json(), indent=4))

+ 48 - 0
ui/padmanager.py

@@ -0,0 +1,48 @@
+import json
+
+import config
+import os
+import sys
+
+class _PadManager:
+
+    def __init__(self):
+        self.pads={}
+        self.list=[]
+        for file in os.listdir(config.PAD_DIRECTORY):
+            if file.lower().endswith(".json") or file.lower().endswith(".conf"):
+                x=self.test_pad(file)
+                if x:
+                    self.pads[x] = file
+                    self.list.append(x)
+
+    def test_pad(self, file):
+        name = None
+        try:
+            with open(os.path.join(config.PAD_DIRECTORY, file)) as f:
+                content=f.read()
+                x=json.loads(content)
+                if x and "name" in x and x["name"]:
+                    return x["name"]
+        except:
+            pass
+        return None
+
+    def read_pad(self, padname):
+        file=os.path.join(config.PAD_DIRECTORY, self.pads[padname])
+        with open(file) as f:
+            return json.loads(f.read())
+
+
+
+class PadManager:
+    __pm = _PadManager()
+    @staticmethod
+    def read_pad(name):
+        return PadManager.__pm.read_pad(name)
+
+    @staticmethod
+    def pad_list():
+        return PadManager.__pm.list
+
+

+ 196 - 0
ui/simplemidi.py

@@ -0,0 +1,196 @@
+import json
+import os
+import socket
+import time
+import subprocess
+from ctypes import Union
+
+from gi.overrides import Gtk
+
+import config
+
+PORT=8081
+
+class SPResponse:
+
+    def __init__(self, code, msg, log):
+        self.code=code
+        self.message=msg
+        self.log=log
+
+    def __bool__(self):
+        return self.code!=0
+
+
+class Socket(socket.socket):
+    def __init__(self):
+        super().__init__(socket.AF_INET, socket.SOCK_STREAM)
+
+    def readc(self, blocking=True):
+        c=b''
+        while not len(c):
+            c=self.recv(1)
+        return c
+
+
+    def readline(self):
+        buffer = bytes()
+        ok=False
+        while not ok:
+            c = self.readc()
+            if c[0]==10: #new line
+                ok=True
+            else:
+                buffer+=bytes(c)
+
+        self.readc()
+        self.readc()
+        return buffer.decode("utf-8")
+
+
+    def response(self):
+        x=self.readline()
+        _status = x.split(" ")
+        code = int(_status[0])
+        msg = " ".join(_status[1:])
+        log = ""
+        ok=False
+        delim=">".encode()
+        while not ok:
+            c=self.readc()
+            if c!=delim:
+               log+=self.readline()+"\n"
+            else:
+                self.readc()
+                break
+        return SPResponse(code, msg, log)
+
+
+    def write(self, string):
+        if not "\n" in string: string += "\r\n"
+        b = string.encode()
+        l = len(b)
+        i = 0
+        while i < l:
+            i += self.send(b[i:])
+
+    def connect(self, address) -> None:
+        x=super().connect(address)
+        self.readc()
+        self.readc()
+        self.readc()
+
+    def set_pad(self, pad):
+        if isinstance(pad, str):
+            self.write('pad select "%s"\n'%pad)
+        else:
+            self.write('pad select %d\n'%pad)
+        return self.response()
+
+    def set_configuration(self, path):
+        self.write('pad load "%s"\n' % path)
+        return self.response()
+
+    def clear(self):
+        self.write('pad clear\n')
+        return self.response()
+
+
+class Response:
+
+    def __init__(self, sock):
+        self.code=0
+        self.message=""
+        self.log=""
+
+
+class DisconnectException(Exception):
+    def __init__(self, string=""):
+        super().__init__(string)
+
+
+class SimpleMidi:
+    TEMP_FILE=".simple_midi_exchange"
+
+    NB_TRIES=200
+    def __init__(self, connect=None):
+        self.connect=connect
+        self.pid=-1
+        self.socket=None
+        self.process=None
+
+
+    def run(self):
+        if not self.connect and not self.is_alive():
+            padsdir = os.path.normpath(os.path.join(os.getcwd(), "../pads"))
+            args=(os.path.join(os.getcwd(), config.PAD_ROUTER_BIN), "--command", "inet", "8081", "-k", "--pads-dir", padsdir)
+            self.process=subprocess.Popen(args)
+            """if (self.pid>0):
+                os.kill(self.pid, 9)
+                os.wait()
+            "ui/" + config.PAD_ROUTER_BIN, ".", "--command", "inet", "8081", "-k", "--pads-dir", padsdir)
+            self.pid = os.fork()
+
+            if not self.pid:
+                Gtk.main_quit()
+                print("Start: ", config.PAD_ROUTER_BIN + " --pads-dir " + SimpleMidi.TEMP_FILE)
+                os.chdir("..")
+                padsdir = os.path.normpath(os.path.join(os.getcwd(), "pads"))
+                args=("ui/" + config.PAD_ROUTER_BIN, ".",  "--command", "inet", "8081", "-k", "--pads-dir", padsdir)
+                print(" ".join(args))
+                os.execl(*args)"""
+
+        self.socket = Socket()
+        for i in range(SimpleMidi.NB_TRIES):
+            try:
+                self.socket.connect(self.connect if self.connect else("127.0.0.1", PORT) )
+                return
+            except:
+                print("Impossible de se connecter, essai %d/%d"%(i+1, SimpleMidi.NB_TRIES))
+                time.sleep(0.5)
+                if not self.is_alive():
+                    raise DisconnectException()
+        raise DisconnectException()
+
+    def clear(self):
+        if not self.is_alive():
+            raise DisconnectException()
+        self.socket.clear()
+
+    def set_pad(self, pad):
+        if isinstance(pad, dict):
+            if "pad" in pad:
+                pad=pad["pad"]
+            elif "name" in pad:
+                pad=pad["name"]
+
+        if not self.is_alive():
+            raise DisconnectException()
+        self.socket.set_pad(pad)
+
+    def set_configuration_data(self, padconf):
+        if not self.is_alive():
+            self.run()
+        with open(SimpleMidi.TEMP_FILE, "w") as f:
+            f.write(json.dumps(padconf.json()))
+        self.socket.set_configuration("ui/"+SimpleMidi.TEMP_FILE)
+
+    def set_configuration_file(self, padconf):
+        if not self.is_alive():
+            self.run()
+        self.socket.set_configuration(padconf)
+
+    def is_alive(self):
+        return self.process and self.process.poll()==None
+
+
+if __name__ == "__main__":
+    sm = SimpleMidi()
+    sm.run()
+    sm.set_pad(0)
+    sm.clear()
+    while True:
+        time.sleep(5)
+        sm.set_configuration_file("ui/conf2.conf")
+        time.sleep(5)
+        sm.set_configuration_file("ui/conf3.conf")

+ 1820 - 0
ui/ui.glade

@@ -0,0 +1,1820 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.38.1 -->
+<interface>
+  <requires lib="gtk+" version="3.24"/>
+  <object class="GtkAction" id="action1"/>
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="lower">1</property>
+    <property name="upper">16</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment2">
+    <property name="upper">127</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment3">
+    <property name="upper">127</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment4">
+    <property name="upper">127</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment5">
+    <property name="lower">-16</property>
+    <property name="upper">100</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment6">
+    <property name="lower">-127</property>
+    <property name="upper">127</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment7">
+    <property name="lower">-127</property>
+    <property name="upper">127</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+  </object>
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-add</property>
+  </object>
+  <object class="GtkImage" id="image10">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-go-up</property>
+  </object>
+  <object class="GtkImage" id="image11">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-delete</property>
+  </object>
+  <object class="GtkImage" id="image12">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-add</property>
+  </object>
+  <object class="GtkImage" id="image13">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-edit</property>
+  </object>
+  <object class="GtkImage" id="image14">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-edit</property>
+  </object>
+  <object class="GtkImage" id="image15">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-go-down</property>
+  </object>
+  <object class="GtkImage" id="image16">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-go-up</property>
+  </object>
+  <object class="GtkImage" id="image17">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-refresh</property>
+  </object>
+  <object class="GtkImage" id="image2">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-apply</property>
+  </object>
+  <object class="GtkImage" id="image21">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-go-down</property>
+  </object>
+  <object class="GtkImage" id="image3">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-delete</property>
+  </object>
+  <object class="GtkImage" id="image4">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-go-up</property>
+  </object>
+  <object class="GtkImage" id="image5">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-add</property>
+  </object>
+  <object class="GtkApplicationWindow" id="main_window">
+    <property name="can-focus">False</property>
+    <property name="default-width">1000</property>
+    <property name="default-height">700</property>
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkMenuBar">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <child>
+              <object class="GtkMenuItem">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes">_Fichier</property>
+                <property name="use-underline">True</property>
+                <child type="submenu">
+                  <object class="GtkMenu">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <child>
+                      <object class="GtkImageMenuItem" id="menu_new">
+                        <property name="label">gtk-new</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="menu_open">
+                        <property name="label">gtk-open</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="menu_save">
+                        <property name="label">gtk-save</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem" id="menu_save_as">
+                        <property name="label">gtk-save-as</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparatorMenuItem">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem">
+                        <property name="label">gtk-quit</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes">É_dition</property>
+                <property name="use-underline">True</property>
+                <child type="submenu">
+                  <object class="GtkMenu">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <child>
+                      <object class="GtkImageMenuItem">
+                        <property name="label">gtk-cut</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem">
+                        <property name="label">gtk-copy</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem">
+                        <property name="label">gtk-paste</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImageMenuItem">
+                        <property name="label">gtk-delete</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes">_Affichage</property>
+                <property name="use-underline">True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuItem">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes">Aid_e</property>
+                <property name="use-underline">True</property>
+                <child type="submenu">
+                  <object class="GtkMenu">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <child>
+                      <object class="GtkImageMenuItem">
+                        <property name="label">gtk-about</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="use-underline">True</property>
+                        <property name="use-stock">True</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkToolbar">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <child>
+              <object class="GtkToolItem">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <child>
+                  <object class="GtkButton" id="toolbox_start">
+                    <property name="label" translatable="yes">Téléverser</property>
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="receives-default">True</property>
+                    <property name="image">image16</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolItem">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <child>
+                  <object class="GtkButton" id="toolbox_clear">
+                    <property name="label" translatable="yes">Reset</property>
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="receives-default">True</property>
+                    <property name="image">image17</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkNotebook" id="notebook">
+            <property name="visible">True</property>
+            <property name="can-focus">True</property>
+            <child>
+              <object class="GtkPaned" id="selection_root_pane">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="resize-mode">immediate</property>
+                <property name="position">301</property>
+                <property name="position-set">True</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="orientation">vertical</property>
+                    <property name="baseline-position">bottom</property>
+                    <child>
+                      <object class="GtkPaned">
+                        <property name="visible">True</property>
+                        <property name="can-focus">True</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkBox" id="sel_pane_root">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <property name="orientation">vertical</property>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="can-focus">False</property>
+                                <property name="halign">start</property>
+                                <property name="label" translatable="yes">Séléctions</property>
+                                <attributes>
+                                  <attribute name="font-desc" value="Sans Bold 10"/>
+                                  <attribute name="weight" value="heavy"/>
+                                </attributes>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="resize">True</property>
+                            <property name="shrink">True</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <property name="orientation">vertical</property>
+                            <child>
+                              <object class="GtkFrame" id="sel_edit_box">
+                                <property name="visible">True</property>
+                                <property name="can-focus">False</property>
+                                <property name="label-xalign">0</property>
+                                <property name="shadow-type">none</property>
+                                <child>
+                                  <object class="GtkBox">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <object class="GtkBox">
+                                        <property name="visible">True</property>
+                                        <property name="can-focus">False</property>
+                                        <child>
+                                          <object class="GtkLabel">
+                                            <property name="visible">True</property>
+                                            <property name="can-focus">False</property>
+                                            <property name="label" translatable="yes">Nom</property>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="fill">True</property>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkBox">
+                                            <property name="visible">True</property>
+                                            <property name="can-focus">False</property>
+                                            <child>
+                                              <object class="GtkEntry" id="entry_sel_name">
+                                                <property name="visible">True</property>
+                                                <property name="can-focus">True</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">True</property>
+                                                <property name="fill">True</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <object class="GtkButton" id="rename_sel">
+                                                <property name="visible">True</property>
+                                                <property name="can-focus">True</property>
+                                                <property name="receives-default">True</property>
+                                                <property name="image">image2</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">True</property>
+                                                <property name="position">1</property>
+                                              </packing>
+                                            </child>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">True</property>
+                                            <property name="fill">True</property>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkNotebook">
+                                        <property name="name">dssd</property>
+                                        <property name="visible">True</property>
+                                        <property name="can-focus">True</property>
+                                        <child>
+                                          <object class="GtkBox">
+                                            <property name="visible">True</property>
+                                            <property name="can-focus">False</property>
+                                            <property name="orientation">vertical</property>
+                                            <child>
+                                              <object class="GtkListBox" id="init_listbox">
+                                                <property name="visible">True</property>
+                                                <property name="can-focus">False</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">True</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <object class="GtkBox">
+                                                <property name="visible">True</property>
+                                                <property name="can-focus">False</property>
+                                                <child>
+                                                  <object class="GtkLabel">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">False</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">True</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">0</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="init_down">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image15</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">1</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="init_up">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image4</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">2</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="init_remove">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image3</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">3</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="init_edit">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image13</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">4</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="init_add">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image1</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">5</property>
+                                                  </packing>
+                                                </child>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">False</property>
+                                                <property name="pack-type">end</property>
+                                                <property name="position">1</property>
+                                              </packing>
+                                            </child>
+                                          </object>
+                                        </child>
+                                        <child type="tab">
+                                          <object class="GtkLabel">
+                                            <property name="visible">True</property>
+                                            <property name="can-focus">False</property>
+                                            <property name="label" translatable="yes">Initialisation</property>
+                                          </object>
+                                          <packing>
+                                            <property name="tab-fill">False</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkBox">
+                                            <property name="visible">True</property>
+                                            <property name="can-focus">False</property>
+                                            <property name="orientation">vertical</property>
+                                            <child>
+                                              <object class="GtkListBox" id="operation_listbox">
+                                                <property name="visible">True</property>
+                                                <property name="can-focus">False</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">True</property>
+                                                <property name="fill">True</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <object class="GtkBox">
+                                                <property name="visible">True</property>
+                                                <property name="can-focus">False</property>
+                                                <child>
+                                                  <object class="GtkLabel">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">False</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">True</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">0</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="operation_down">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image21</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">1</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="operation_up">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image10</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">2</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="operation_remove">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image11</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">3</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="operation_edit">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image14</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">4</property>
+                                                  </packing>
+                                                </child>
+                                                <child>
+                                                  <object class="GtkButton" id="operation_add">
+                                                    <property name="visible">True</property>
+                                                    <property name="can-focus">True</property>
+                                                    <property name="receives-default">True</property>
+                                                    <property name="image">image12</property>
+                                                  </object>
+                                                  <packing>
+                                                    <property name="expand">False</property>
+                                                    <property name="fill">True</property>
+                                                    <property name="position">5</property>
+                                                  </packing>
+                                                </child>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">True</property>
+                                                <property name="position">1</property>
+                                              </packing>
+                                            </child>
+                                          </object>
+                                          <packing>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                        <child type="tab">
+                                          <object class="GtkLabel">
+                                            <property name="visible">True</property>
+                                            <property name="can-focus">False</property>
+                                            <property name="label" translatable="yes">Opération</property>
+                                          </object>
+                                          <packing>
+                                            <property name="position">1</property>
+                                            <property name="tab-fill">False</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child type="tab">
+                                          <object class="GtkLabel">
+                                            <property name="visible">True</property>
+                                            <property name="can-focus">False</property>
+                                            <property name="label" translatable="yes">Opérations</property>
+                                          </object>
+                                          <packing>
+                                            <property name="position">2</property>
+                                            <property name="tab-fill">False</property>
+                                          </packing>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">True</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child type="label">
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                    <property name="label" translatable="yes">Proprietés</property>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="resize">True</property>
+                            <property name="shrink">True</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="valign">end</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <property name="halign">start</property>
+                            <property name="label" translatable="yes">Ajouter une sélection</property>
+                            <attributes>
+                              <attribute name="gravity" value="west"/>
+                              <attribute name="gravity-hint" value="strong"/>
+                            </attributes>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <child>
+                              <object class="GtkEntry" id="input_new_pad">
+                                <property name="visible">True</property>
+                                <property name="can-focus">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkButton" id="button_new_sel">
+                                <property name="visible">True</property>
+                                <property name="can-focus">True</property>
+                                <property name="receives-default">True</property>
+                                <property name="image">image5</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="resize">True</property>
+                    <property name="shrink">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkBox" id="grid_root">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="resize">True</property>
+                    <property name="shrink">True</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes">Pad</property>
+              </object>
+              <packing>
+                <property name="tab-fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes">Configuration</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+                <property name="tab-fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="label" translatable="yes">page 3</property>
+              </object>
+              <packing>
+                <property name="position">2</property>
+                <property name="tab-fill">False</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkImage" id="image6">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-apply</property>
+  </object>
+  <object class="GtkImage" id="image7">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-cancel</property>
+  </object>
+  <object class="GtkDialog" id="init_dialog">
+    <property name="can-focus">False</property>
+    <property name="modal">True</property>
+    <property name="destroy-with-parent">True</property>
+    <property name="type-hint">dialog</property>
+    <property name="skip-taskbar-hint">True</property>
+    <property name="has-resize-grip">True</property>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can-focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can-focus">False</property>
+            <property name="layout-style">end</property>
+            <child>
+              <object class="GtkButton" id="init_cancel">
+                <property name="label" translatable="yes">Annuler</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="image">image7</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="init_ok">
+                <property name="label" translatable="yes"> OK</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="image">image6</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes">Message</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBoxText" id="init_message">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="active">0</property>
+                    <items>
+                      <item id="noteon" translatable="yes">NoteOn</item>
+                      <item id="noteoff" translatable="yes">NoteOff</item>
+                    </items>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkListBox">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <child>
+                  <object class="GtkListBoxRow">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <child>
+                          <object class="GtkCheckButton" id="init_enable_channel">
+                            <property name="label" translatable="yes"> </property>
+                            <property name="visible">True</property>
+                            <property name="can-focus">True</property>
+                            <property name="receives-default">False</property>
+                            <property name="draw-indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <property name="label" translatable="yes"> Cannal </property>
+                          </object>
+                          <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="init_channel_value">
+                            <property name="visible">True</property>
+                            <property name="can-focus">True</property>
+                            <property name="progress-fraction">1</property>
+                            <property name="progress-pulse-step">1</property>
+                            <property name="adjustment">adjustment1</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkListBoxRow">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <child>
+                          <object class="GtkCheckButton" id="init_enable_key">
+                            <property name="label" translatable="yes"> </property>
+                            <property name="visible">True</property>
+                            <property name="can-focus">True</property>
+                            <property name="receives-default">False</property>
+                            <property name="draw-indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <property name="label" translatable="yes"> Clé </property>
+                          </object>
+                          <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="init_key_value">
+                            <property name="visible">True</property>
+                            <property name="can-focus">True</property>
+                            <property name="adjustment">adjustment2</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkListBoxRow">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <child>
+                          <object class="GtkCheckButton" id="init_enable_velocity">
+                            <property name="label" translatable="yes"> </property>
+                            <property name="visible">True</property>
+                            <property name="can-focus">True</property>
+                            <property name="receives-default">False</property>
+                            <property name="draw-indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <property name="label" translatable="yes"> Valeur </property>
+                          </object>
+                          <packing>
+                            <property name="expand">True</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="init_velocity_value">
+                            <property name="visible">True</property>
+                            <property name="can-focus">True</property>
+                            <property name="adjustment">adjustment3</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkImage" id="image8">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-apply</property>
+  </object>
+  <object class="GtkImage" id="image9">
+    <property name="visible">True</property>
+    <property name="can-focus">False</property>
+    <property name="stock">gtk-cancel</property>
+  </object>
+  <object class="GtkDialog" id="operation_dialog">
+    <property name="can-focus">False</property>
+    <property name="type-hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can-focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can-focus">False</property>
+            <property name="layout-style">end</property>
+            <child>
+              <object class="GtkButton" id="operation_cancel">
+                <property name="label" translatable="yes">Annuler</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="image">image9</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="operation_ok">
+                <property name="label" translatable="yes"> OK</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="image">image8</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes">Opération</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBoxText" id="operation_type">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="active">0</property>
+                    <items>
+                      <item id="TRANSLATE" translatable="yes">Translation</item>
+                      <item id="DROP" translatable="yes">Drop</item>
+                      <item id="RANGE" translatable="yes">Gamme</item>
+                      <item id="REORDER" translatable="yes">Changer l'ordre</item>
+                    </items>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkFrame" id="operation_drop">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label-xalign">0</property>
+                    <property name="shadow-type">none</property>
+                    <child>
+                      <object class="GtkAlignment">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="left-padding">12</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="label" translatable="yes">Drop</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="operation_reorder">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label-xalign">0</property>
+                    <property name="shadow-type">none</property>
+                    <child>
+                      <object class="GtkAlignment">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="left-padding">12</property>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="can-focus">False</property>
+                                <property name="label" translatable="yes"> Offset </property>
+                              </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                            <child>
+                              <object class="GtkSpinButton" id="operation_offset">
+                                <property name="visible">True</property>
+                                <property name="can-focus">True</property>
+                                <property name="adjustment">adjustment4</property>
+                              </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="label" translatable="yes">Changer d'ordre</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="operation_range">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label-xalign">0</property>
+                    <property name="shadow-type">none</property>
+                    <child>
+                      <object class="GtkAlignment">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="left-padding">12</property>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <property name="orientation">vertical</property>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="can-focus">False</property>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                    <property name="label" translatable="yes"> Note </property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">True</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkComboBoxText" id="operation_gamme_note">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkComboBoxText" id="operation_gamme_note_octave">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                    <items>
+                                      <item id="0" translatable="yes">0</item>
+                                      <item id="1" translatable="yes">1</item>
+                                      <item id="2" translatable="yes">2</item>
+                                      <item id="3" translatable="yes">3</item>
+                                      <item id="4" translatable="yes">4</item>
+                                      <item id="5" translatable="yes">5</item>
+                                      <item id="6" translatable="yes">6</item>
+                                      <item id="7" translatable="yes">7</item>
+                                      <item id="8" translatable="yes">8</item>
+                                      <item id="9" translatable="yes">9</item>
+                                    </items>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">3</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="can-focus">False</property>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                    <property name="label" translatable="yes"> Gamme</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkComboBoxText" id="operation_gamme_gamme">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">True</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="can-focus">False</property>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                    <property name="label" translatable="yes"> Personnalisée (ex: 0,2,3,4)</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkEntry" id="operation_gamme_custom">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">True</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="label" translatable="yes">Gamme</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="operation_translate">
+                    <property name="visible">True</property>
+                    <property name="can-focus">False</property>
+                    <property name="label-xalign">0</property>
+                    <property name="shadow-type">none</property>
+                    <child>
+                      <object class="GtkAlignment">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="left-padding">12</property>
+                        <child>
+                          <object class="GtkBox">
+                            <property name="visible">True</property>
+                            <property name="can-focus">False</property>
+                            <property name="orientation">vertical</property>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="can-focus">False</property>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                    <property name="label" translatable="yes">Cannal  </property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="operation_translate_channel">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">True</property>
+                                    <property name="adjustment">adjustment5</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkCheckButton" id="operation_translate_channel_absolute">
+                                    <property name="label" translatable="yes">Absolu</property>
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">True</property>
+                                    <property name="receives-default">False</property>
+                                    <property name="draw-indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">3</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="can-focus">False</property>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                    <property name="label" translatable="yes"> Note    </property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="operation_translate_key">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">True</property>
+                                    <property name="adjustment">adjustment6</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkCheckButton" id="operation_translate_key_absolute">
+                                    <property name="label" translatable="yes">Absolu</property>
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">True</property>
+                                    <property name="receives-default">False</property>
+                                    <property name="draw-indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">3</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="can-focus">False</property>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">False</property>
+                                    <property name="label" translatable="yes">Valeur  </property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="operation_translate_velocity">
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">True</property>
+                                    <property name="adjustment">adjustment7</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkCheckButton" id="operation_translate_velocity_absolute">
+                                    <property name="label" translatable="yes">Absolu</property>
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">True</property>
+                                    <property name="receives-default">False</property>
+                                    <property name="draw-indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">3</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="label" translatable="yes">Translaation</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkWindow" id="new_pad_conf_dialog">
+    <property name="can-focus">False</property>
+    <child>
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="can-focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="label" translatable="yes">Sélectionner un pad :</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkComboBoxText" id="cb_new_pad">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <child>
+              <placeholder/>
+            </child>
+            <child>
+              <object class="GtkButton" id="new_pad">
+                <property name="label" translatable="yes">Valider</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">4</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>