Compare commits

...

14 commits

Author SHA1 Message Date
zawz
199390d9fa update for newer ztd version 2021-08-12 18:37:44 +02:00
zawz
9efd8cf9af Output to mim format + bugfixes 2020-05-22 16:15:48 +02:00
zawz
9e984f6d38 Update to new ztd 2020-05-03 17:03:45 +02:00
zawz
df123ef150 Better help gen 2020-03-03 13:23:48 +01:00
zawz
f10a6900c6 Move to new zfd format 2020-03-03 10:25:14 +01:00
zawz
22d3462f09 Fix command count 2020-02-18 16:50:47 +01:00
zawz
92cf2c603c Fix stdin mim 2020-02-18 16:50:18 +01:00
zawz
6ff844b772 Changed stdin method 2020-02-18 16:17:54 +01:00
zawz
2b8da6aa30 Enable optimisations 2020-02-13 23:18:51 +01:00
zawz
99f98b165a strval cleanup 2020-02-13 23:18:33 +01:00
zawz
0d7213d41a Code cleanup 2020-01-31 15:46:17 +01:00
zawz
8c944a49af add gitignore 2019-11-01 07:42:17 +01:00
zawz
b742fa5ae5 README dependency fix 2019-09-26 11:51:08 +02:00
zawz
1c8c3ac9d2 v1.3 : added auto file reloading
+ Files now auto reload when changes are detected
+ Added --no-reload option to disable auto reloading
* Internal cleanups
2019-09-26 11:42:11 +02:00
20 changed files with 354 additions and 196 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
zmidimap
obj
Zmakefile

View file

@ -11,6 +11,8 @@ CC=g++
CXXFLAGS= -I$(IDIR) -Wall -pedantic -std=c++17 CXXFLAGS= -I$(IDIR) -Wall -pedantic -std=c++17
ifeq ($(DEBUG),true) ifeq ($(DEBUG),true)
CXXFLAGS += -g CXXFLAGS += -g
else
CXXFLAGS += -O2
endif endif
ifeq ($(STATIC),true) ifeq ($(STATIC),true)
LDFLAGS += -l:libztd.a LDFLAGS += -l:libztd.a

View file

@ -2,7 +2,7 @@
Map midi signals coming from ALSA midi devices to shell commands Map midi signals coming from ALSA midi devices to shell commands
Dependencies: alsa-utils , [ztd](https://github.com/zawwz/ztd) Dependencies: alsa-utils
## Installing ## Installing
@ -14,10 +14,12 @@ Download the .deb package then run: `sudo dpkg -i zmidimap.deb ; sudo apt -f ins
### Standalone ### Standalone
Download ``zmidimap-static.tar.gz`` for a standalone executable without library dependency Download ``zmidimap.tar.gz`` for a standalone executable
### From source ### From source
Depends on: [ztd](https://github.com/zawwz/ztd)
``make -j6`` for a shared build ``make -j6`` for a shared build
``STATIC=true make -j6`` for a static build ``STATIC=true make -j6`` for a static build

View file

@ -1,49 +1,43 @@
Device "Launch Control" Device "Launch Control"
//display value 0:127 for knobs 21-28 //display value 0:127 for knobs 21-28
Command controller Command controller
id=21:28 id=21:28
shell=echo "Knob #$id ch$channel:$value" shell='echo "Knob #$id ch$channel:$value"'
//display value 0:127 for knob 41 from any channel //display value -100:100 for knob 41 on channel 0
Command controller Command controller
id=41 id=41
channel=*
shell=echo "Knob #$id ch$channel:$value"
//display value -100:100 for knob 42 on channel 0
Command controller
id=42
channel=0 channel=0
remap=-100:100 remap=-100:100
shell=echo "Knob #$id ch0:$value r:$rawvalue" shell='echo "Knob #$id ch0:$value r:$rawvalue"'
//display value 0:1:0 for knob 42 on channel 1 (first half) //display value 0:1:0 for knob 41 on channel 1 (first half)
Command controller Command controller
id=42 id=42
channel=1 channel=1
range=0:63 range=0:63
remap=0:1 remap=0:1
float=true float=true
shell=echo "Knob #$id ch1:$value r:$rawvalue" shell='echo "Knob #$id ch1:$value r:$rawvalue"'
//display value 0:1:0 for knob 42 on channel 1 (second half) //display value 0:1:0 for knob 41 on channel 1 (second half)
Command controller Command controller
id=42 id=42
channel=1 channel=1
range=64:127 range=64:127
remap=1:0 remap=1:0
float=true float=true
shell=echo "Knob #$id ch1:$value r:$rawvalue" shell='echo "Knob #$id ch1:$value r:$rawvalue"'
Device "Launchpad S" Device "Launchpad S"
//display any note pressed and its velocity
Command note Command note
shell=echo "Note $id on velocity:$velocity" shell=echo Note $id on velocity:$velocity
//display any note off
Command note Command note
trigger=0 trigger=0
shell=echo "Note $id off" shell=echo Note $id off

View file

@ -10,46 +10,38 @@
//displays value 0:127 for knobs 21-28 //displays value 0:127 for knobs 21-28
type=controller type=controller
id=21:28 id=21:28
shell=echo "Knob #$id ch$channel:$value" shell='echo "Knob #$id ch$channel:$value"'
},
// KNOBS LOW
{
//KNOB L1
//displays value 0:127 for knob 41 from any channel
type=controller
id=41
shell=echo "Knob #$id ch$channel:$value"
}, },
{ {
//KNOB L2 //KNOB L2
//displays value -100:100 for knob 42 on channel 0 //displays value -100:100 for knob 41 on channel 0
type=controller type=controller
id=42 id=42
channel=0 channel=0
remap=-100:100 remap=-100:100
shell=echo "Knob #$id ch0:$value r:$rawvalue" shell='echo "Knob #$id ch0:$value r:$rawvalue"'
}, },
{ {
//KNOB L3 H1 //KNOB L3 H1
//displays value 0:1:0 for knob 42 on channel 1 (first half) //displays value 0:1:0 for knob 41 on channel 1 (first half)
type=controller type=controller
id=42 id=42
channel=1 channel=1
range=0:63 range=0:63
remap=0:1 remap=0:1
float=true float=true
shell=echo "Knob #$id ch1:$value r:$rawvalue" shell='echo "Knob #$id ch1:$value r:$rawvalue"'
}, },
{ {
//KNOB L3 H2 //KNOB L3 H2
//displays value 0:1:0 for knob 42 on channel 1 (second half) //displays value 0:1:0 for knob 41 on channel 1 (second half)
type=controller type=controller
id=42 id=42
channel=1 channel=1
range=64:127 range=64:127
remap=1:0 remap=1:0
float=true float=true
shell=echo "Knob #$id ch1:$value r:$rawvalue" shell='echo "Knob #$id ch1:$value r:$rawvalue"'
} }
] ]
} }
@ -61,13 +53,13 @@
{ {
// ANY NOTE ON // ANY NOTE ON
type=note type=note
shell=echo "Note $id on velocity:$velocity" shell=echo Note $id on velocity:$velocity
}, },
{ {
// ANY NOTE OFF // ANY NOTE OFF
type=note type=note
trigger=0 trigger=0
shell=echo "Note $id off" shell=echo Note $id off
} }
] ]
} }

View file

@ -1,11 +1,38 @@
FORMAT_FOLDER=help_format FORMAT_FOLDER=help_format
IDIR=include IDIR=include
cp $FORMAT_FOLDER/help_template_head $IDIR/help.h SLASHSCRIPT='s|\\|\\\\|g;s|\"|\\\"|g'
NEWLINESCRIPT=':a;N;$!ba;s/\n/\\n/g;'
echo "#define ZFD_FORMAT \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/zfd-format)\"" >> $IDIR/help.h filetocstr()
echo "#define MIM_FORMAT \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/mim-format)\"" >> $IDIR/help.h {
echo "#define SHELL_FORMAT \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/shell-format)\"" >> $IDIR/help.h sed -e $SLASHSCRIPT "$1" | sed $NEWLINESCRIPT
echo "#define COMMAND_TAGS \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/command-tags)\"" >> $IDIR/help.h }
cat $FORMAT_FOLDER/help_template_tail >> $IDIR/help.h gen_line()
{
name="$(basename "$1")"
echo "#define $(echo "$name" | tr '[:lower:]' '[:upper:]') \"$(filetocstr "$1")\""
}
help_header()
{
echo '#ifndef HELP_H
#define HELP_
'
}
help_footer()
{
echo '
#endif //HELP_H'
}
help_header > "$IDIR/help.h"
for I in help_format/*
do
gen_line "$I" >> "$IDIR/help.h"
done
help_footer >> "$IDIR/help.h"

View file

@ -1,2 +0,0 @@
#ifndef HELP_H
#define HELP_H

View file

@ -1 +0,0 @@
#endif //HELP_H

View file

@ -20,6 +20,7 @@ Device <name>
*shell: shell command to be executed *shell: shell command to be executed
> mandatory > mandatory
Shell command can be concatenated with \"\" or '' Shell commands can be concatenated with "" or ''
Comments are written by starting a line with // Comments are written with // or # and end at end of line
see --command-tags for optional command tags Multiple tags can be put on the same line by separating them with ;
See --command-tags for optional command tags

View file

@ -1,5 +1,7 @@
-- [ZFD FILE FORMAT] -- -- [ZFD FILE FORMAT] --
ZFD format: http://zawz.net/doc/ztd/zfd.html
[<device>,<device>] [<device>,<device>]
-- <device> format -- -- <device> format --
@ -24,5 +26,5 @@
*shell: shell command to be executed *shell: shell command to be executed
> mandatory > mandatory
Shell command can be concatenated with \"\" or '' Shell commands can be concatenated with "" or ''
see --command-tags for optional command tags see --command-tags for optional command tags

View file

@ -8,8 +8,6 @@
#include "command.hpp" #include "command.hpp"
#include <ztd/filedat.hpp> #include <ztd/filedat.hpp>
void sh(std::string const& string);
class Device class Device
{ {
public: public:
@ -19,7 +17,7 @@ public:
bool start_loop(); bool start_loop();
void run_signal(char* buff); void run_signal(char* buff);
bool import_chunk(ztd::chunkdat const& ch); bool import_chunk(const ztd::chunkdat& ch);
ztd::chunkdat export_chunk(); ztd::chunkdat export_chunk();
std::string name; std::string name;
@ -34,7 +32,6 @@ public:
std::vector<ConnectCommand> connectCommands; std::vector<ConnectCommand> connectCommands;
std::vector<DisconnectCommand> disconnectCommands; std::vector<DisconnectCommand> disconnectCommands;
std::thread thread;
pid_t thread_pid; pid_t thread_pid;
private: private:
static void loop(Device* dev); static void loop(Device* dev);
@ -42,4 +39,6 @@ private:
extern std::vector<Device*> device_list; extern std::vector<Device*> device_list;
void clean_devices();
#endif //DEVICE_HPP #endif //DEVICE_HPP

View file

@ -3,7 +3,9 @@
#include <ztd/filedat.hpp> #include <ztd/filedat.hpp>
ztd::chunkdat mimtochk(const std::string& mim); ztd::chunkdat mimtochk(std::string mim);
std::string chktomim(ztd::chunkdat const& chk, std::string const& aligner);
std::string file_strimport(const std::string& path); std::string file_strimport(const std::string& path);

View file

@ -1,7 +1,9 @@
#ifndef HELP_H #ifndef HELP_H
#define HELP_H #define HELP_
#define ZFD_FORMAT "zmidimap\n-- [ZFD FILE FORMAT] --\n\n[<device>,<device>]\n\n-- <device> format --\n {\n name=<name>\n commands=[<command>,<command>]\n }\n--\n*name: string referring to client name of the device\n\n<command> format\n {\n type=<type>\n shell=<shell command>\n <tag>=<value>\n <tag>=<value>\n ...\n }\n--\n*type: type of the signal: note/controller/pitch/system/connect/disconnect\n > mandatory\n*shell: shell command to be executed\n > mandatory\n\nShell command can be concatenated with \"\" or ''\nsee --command-tags for optional command tags"
#define MIM_FORMAT "zmidimap\n-- [MIM FILE FORMAT] --\n\nDevice <name>\n Command <type>\n shell=<shell command>\n <tag>=<value>\n <tag>=<value>\n ...\n\nDevice <name>\n...\n\n--\n*name: string referring to client name of the device\n > mandatory\n*type: type of the signal: note/controller/pitch/system/connect/disconnect\n > mandatory\n*shell: shell command to be executed\n > mandatory\n\nShell command can be concatenated with \"\" or ''\nsee --command-tags for optional command tags" #define COMMAND_TAGS "-- [COMMAND TAGS] --\n\n[Note tags]\n id=<interval>\n channel=<x/*>\n trigger=<interval>\n--\n *id: note id from 0 to 127\n > optional, default 0:127\n *channel: value from 0 to 16 for channel, * for any channel\n > optional, default *\n *trigger: note velocity from 0 to 127 that triggers the command\n > optional, default 1:127\n\n[Controller tags]\n id=<interval>\n channel=<x/*>\n range=<interval>\n remap=<interval>\n float=<true/false>\n--\n *id: controller id from 0 to 127\n > optional, default 0:127\n *channel: value from 0 to 16 for channel, * for any channel\n > optional, default *\n *range: controller value from 0 to 127 that triggers command\n > optional, default 0:127\n *remap: remaps the range to given interval\n > optional, default same as range\n *float: boolean value defining if output is a floating point value\n > optional, default false\n\n[Pitch bend tags]\n range=<interval>\n remap=<interval>\n float=<true/false>\n--\n *range: controller value from -8192 to 8191 that triggers command\n > optional, default -8192:8191\n *remap: remaps the range to given interval\n > optional, default same as range\n *float: boolean value defining if output is a floating point value\n > optional, default false\n\n[Interval Format]\n x:y range from x to y\n x single value x\n * all possible values"
#define SHELL_FORMAT "zmidimap\n-- [SHELL FORMAT] --\n\nShell command follows regular shell format\n\n-- [Environment] --\n\n[Note]\n $id: id of the note\n $channel: channel of the note\n $velocity: velocity of the note\n\n[Controller]\n $value: value of the controller (remapped)\n $id: id of the controller\n $channel: channel of the controller\n $rawvalue: original value of the controller\n\n[Pitch]\n $value: value of the bend (remapped)\n $rawvalue: original value of the bend\n\n[System]\n $code: hexadecimal code of the system signal" #define MIM_FORMAT "-- [MIM FILE FORMAT] --\n\nDevice <name>\n Command <type>\n shell=<shell command>\n <tag>=<value>\n <tag>=<value>\n ...\n Command <type>\n ...\n\nDevice <name>\n...\n\n--\n*name: string referring to client name of the device\n > mandatory\n*type: type of the signal: note/controller/pitch/system/connect/disconnect\n > mandatory\n*shell: shell command to be executed\n > mandatory\n\nShell commands can be concatenated with \"\" or ''\nComments are written with // or # and end at end of line\nMultiple tags can be put on the same line by separating them with ;\nSee --command-tags for optional command tags"
#define COMMAND_TAGS "zmidimap\n-- [COMMAND TAGS] --\n\n[Note tags]\n id=<interval>\n channel=<x/*>\n trigger=<interval>\n--\n *id: note id from 0 to 127\n > optional, default 0:127\n *channel: value from 0 to 16 for channel, * for any channel\n > optional, default *\n *trigger: note velocity from 0 to 127 that triggers the command\n > optional, default 1:127\n\n[Controller tags]\n id=<interval>\n channel=<x/*>\n range=<interval>\n remap=<interval>\n float=<true/false>\n--\n *id: controller id from 0 to 127\n > optional, default 0:127\n *channel: value from 0 to 16 for channel, * for any channel\n > optional, default *\n *range: controller value from 0 to 127 that triggers command\n > optional, default 0:127\n *remap: remaps the range to given interval\n > optional, default same as range\n *float: boolean value defining if output is a floating point value\n > optional, default false\n\n[Pitch bend tags]\n range=<interval>\n remap=<interval>\n float=<true/false>\n--\n *range: controller value from -8192 to 8191 that triggers command\n > optional, default -8192:8191\n *remap: remaps the range to given interval\n > optional, default same as range\n *float: boolean value defining if output is a floating point value\n > optional, default false\n\n[Interval Format]\n x:y range from x to y\n x single value x\n * all possible values" #define SHELL_FORMAT "-- [SHELL FORMAT] --\n\nShell command follows regular shell format\n\n-- [Environment] --\n\n[Note]\n $id: id of the note\n $channel: channel of the note\n $velocity: velocity of the note\n\n[Controller]\n $value: value of the controller (remapped)\n $id: id of the controller\n $channel: channel of the controller\n $rawvalue: original value of the controller\n\n[Pitch]\n $value: value of the bend (remapped)\n $rawvalue: original value of the bend\n\n[System]\n $code: hexadecimal code of the system signal"
#define ZFD_FORMAT "-- [ZFD FILE FORMAT] --\n\nZFD format: http://zawz.net/doc/ztd/zfd.html\n\n[<device>,<device>]\n\n-- <device> format --\n {\n name=<name>\n commands=[<command>,<command>]\n }\n--\n*name: string referring to client name of the device\n\n<command> format\n {\n type=<type>\n shell=<shell command>\n <tag>=<value>\n <tag>=<value>\n ...\n }\n--\n*type: type of the signal: note/controller/pitch/system/connect/disconnect\n > mandatory\n*shell: shell command to be executed\n > mandatory\n\nShell commands can be concatenated with \"\" or ''\nsee --command-tags for optional command tags"
#endif //HELP_H #endif //HELP_H

View file

@ -5,10 +5,16 @@
#define LIST_COMMAND "aseqdump -l | tail -n +2 | cut -c10-42 | tr -s ' '" #define LIST_COMMAND "aseqdump -l | tail -n +2 | cut -c10-42 | tr -s ' '"
#define LIST_EXTENDED_COMMAND "aseqdump -l | tail -n +2 | cut -c-42" #define LIST_EXTENDED_COMMAND "aseqdump -l | tail -n +2 | cut -c-42"
#include <string>
extern int announce_thread_pid; extern int announce_thread_pid;
void kill_all();
void device_check(); void device_check();
void announce_loop(); void announce_loop();
void filetime_loop(std::string const& filepath);
#endif //SYSTEM_HPP #endif //SYSTEM_HPP

View file

@ -5,6 +5,7 @@
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <iostream> #include <iostream>
#include <ztd/shell.hpp> #include <ztd/shell.hpp>
#include "log.hpp" #include "log.hpp"
@ -35,16 +36,11 @@ static std::string dequote(const std::string& in)
return ret; return ret;
} }
static bool _isNum(char a) static bool _isNum(const char& a)
{ {
return (a>='0' && a<='9'); return (a>='0' && a<='9');
} }
void sh(std::string const& string)
{
system(string.c_str());
}
Device::Device() Device::Device()
{ {
busy=false; busy=false;
@ -63,17 +59,16 @@ bool Device::start_loop()
if(this->busy) if(this->busy)
return false; return false;
this->busy = true; this->busy = true;
this->thread = std::thread(Device::loop, this); std::thread(Device::loop, this).detach();
this->thread.detach();
return true; return true;
} }
std::pair<int32_t,int32_t> importRange(ztd::chunkdat const& ch, std::string const& tag, int32_t low, int32_t high) std::pair<int32_t,int32_t> importRange(const ztd::chunkdat& ch, const std::string& tag, int32_t low, int32_t high)
{ {
ztd::chunkdat* pch=ch.subChunkPtr(tag); ztd::chunkdat* pch=ch.subChunkPtr(tag);
if(pch != nullptr) if(pch != nullptr)
{ {
std::string str=pch->strval(); std::string str=*pch;
auto tpos=str.find(':'); auto tpos=str.find(':');
if (str=="*") //whole range if (str=="*") //whole range
{ {
@ -81,25 +76,25 @@ std::pair<int32_t,int32_t> importRange(ztd::chunkdat const& ch, std::string cons
} }
else if(tpos == std::string::npos) //single value else if(tpos == std::string::npos) //single value
{ {
low=stoi(str); low=std::stoi(str);
high=low; high=low;
} }
else //range else //range
{ {
low=stoi(str.substr(0,tpos)); low=std::stoi(str.substr(0,tpos));
tpos++; tpos++;
high=stoi(str.substr(tpos, str.size()-tpos)); high=std::stoi(str.substr(tpos, str.size()-tpos));
} }
} }
return std::make_pair(low, high); return std::make_pair(low, high);
} }
std::pair<float,float> importRangeFloat(ztd::chunkdat const& ch, std::string const& tag, float low, float high) std::pair<float,float> importRangeFloat(const ztd::chunkdat& ch, const std::string& tag, float low, float high)
{ {
ztd::chunkdat* pch=ch.subChunkPtr(tag); ztd::chunkdat* pch=ch.subChunkPtr(tag);
if(pch != nullptr) if(pch != nullptr)
{ {
std::string str=pch->strval(); std::string str=*pch;
auto tpos=str.find(':'); auto tpos=str.find(':');
if(tpos == std::string::npos) if(tpos == std::string::npos)
{ {
@ -118,12 +113,12 @@ std::pair<float,float> importRangeFloat(ztd::chunkdat const& ch, std::string con
return std::make_pair(low, high); return std::make_pair(low, high);
} }
bool importBool(ztd::chunkdat const& ch, std::string const& tag, bool defbool) bool importBool(const ztd::chunkdat& ch, const std::string& tag, const bool& defbool)
{ {
ztd::chunkdat* pch=ch.subChunkPtr(tag); ztd::chunkdat* pch=ch.subChunkPtr(tag);
if(pch != nullptr) if(pch != nullptr)
{ {
std::string str=pch->strval(); std::string str=*pch;
if( str == "true" ) if( str == "true" )
return true; return true;
else if( str == "false" ) else if( str == "false" )
@ -132,30 +127,30 @@ bool importBool(ztd::chunkdat const& ch, std::string const& tag, bool defbool)
return defbool; return defbool;
} }
bool Device::import_chunk(ztd::chunkdat const& ch) bool Device::import_chunk(const ztd::chunkdat& ch)
{ {
ztd::chunkdat& cch = ch["commands"]; ztd::chunkdat& cch = ch["commands"];
this->name=dequote(ch["name"].strval()); this->name=dequote(ch["name"]);
for(int i=0 ; i<cch.listSize() ; i++) for(int i=0 ; i<cch.listSize() ; i++)
{ {
ztd::chunkdat& tch=cch[i]; ztd::chunkdat& tch=cch[i];
std::string tstr=tch["type"].strval(); std::string tstr=tch["type"];
if(tstr == "system") //type system if(tstr == "system") //type system
{ {
std::string shell; std::string shell;
shell=tch["shell"].strval(); shell=tch["shell"];
this->sysCommands.push_back(SystemCommand(shell)); this->sysCommands.push_back(SystemCommand(shell));
} }
else if (tstr == "connect") else if (tstr == "connect")
{ {
std::string shell; std::string shell;
shell=tch["shell"].strval(); shell=tch["shell"];
this->connectCommands.push_back(ConnectCommand(shell)); this->connectCommands.push_back(ConnectCommand(shell));
} }
else if (tstr == "disconnect") else if (tstr == "disconnect")
{ {
std::string shell; std::string shell;
shell=tch["shell"].strval(); shell=tch["shell"];
this->disconnectCommands.push_back(DisconnectCommand(shell)); this->disconnectCommands.push_back(DisconnectCommand(shell));
} }
else else
@ -167,13 +162,13 @@ bool Device::import_chunk(ztd::chunkdat const& ch)
std::pair<float,float> floatpair; std::pair<float,float> floatpair;
//channel //channel
if(tch.subChunkPtr("channel") == nullptr || tch["channel"].strval()=="*") if(tch.subChunkPtr("channel") == nullptr || tch["channel"] == "*")
channel=-1; channel=-1;
else else
channel=stoi(tch["channel"].strval()); channel=std::stoi(tch["channel"]);
//shell //shell
shell=tch["shell"].strval(); shell=tch["shell"];
//type //type
if(tstr == "note") //type note if(tstr == "note") //type note
@ -182,13 +177,11 @@ bool Device::import_chunk(ztd::chunkdat const& ch)
uint8_t id_high=127; uint8_t id_high=127;
uint8_t low=1; uint8_t low=1;
uint8_t high=127; uint8_t high=127;
std::string tt;
//id //id
intpair=importRange(tch, "id", id_low, id_high); intpair=importRange(tch, "id", id_low, id_high);
id_low=intpair.first; id_low=intpair.first;
id_high=intpair.second; id_high=intpair.second;
// id=stoi(tch["id"].strval());
//trigger //trigger
intpair = importRange(tch, "trigger", low, high); intpair = importRange(tch, "trigger", low, high);
@ -199,7 +192,6 @@ bool Device::import_chunk(ztd::chunkdat const& ch)
{ {
this->noteCommands[i].push_back(NoteCommand(channel,low,high,shell)); this->noteCommands[i].push_back(NoteCommand(channel,low,high,shell));
} }
this->nb_command++;
} }
else if(tstr == "controller") //type controller else if(tstr == "controller") //type controller
{ {
@ -233,7 +225,6 @@ bool Device::import_chunk(ztd::chunkdat const& ch)
{ {
this->ctrlCommands[i].push_back(ControllerCommand(channel,min,max,mapMin,mapMax,floating,shell)); this->ctrlCommands[i].push_back(ControllerCommand(channel,min,max,mapMin,mapMax,floating,shell));
} }
this->nb_command++;
} }
else if(tstr == "pitch") //type pitch bend else if(tstr == "pitch") //type pitch bend
{ {
@ -257,7 +248,6 @@ bool Device::import_chunk(ztd::chunkdat const& ch)
floating = importBool(tch, "float", floating); floating = importBool(tch, "float", floating);
this->pitchCommands.push_back(PitchCommand(channel,min,max,mapMin,mapMax,floating,shell)); this->pitchCommands.push_back(PitchCommand(channel,min,max,mapMin,mapMax,floating,shell));
this->nb_command++;
} }
else else
{ {
@ -265,6 +255,7 @@ bool Device::import_chunk(ztd::chunkdat const& ch)
return false; return false;
} }
} }
this->nb_command++;
} }
return true; return true;
} }
@ -295,7 +286,7 @@ void Device::run_signal(char* buff)
{ {
std::string command="code=" + strval + ";"; std::string command="code=" + strval + ";";
command += dequote(it.shell); command += dequote(it.shell);
std::thread(sh, command).detach(); std::thread(ztd::shr, command).detach();
} }
} }
else else
@ -303,7 +294,7 @@ void Device::run_signal(char* buff)
int t; int t;
char type; char type;
int8_t channel; int8_t channel;
int ctid; int8_t ctid=-1;
int16_t value; int16_t value;
char* pos=NULL; char* pos=NULL;
bool note_off=false; bool note_off=false;
@ -322,7 +313,7 @@ void Device::run_signal(char* buff)
else else
{ {
// throw std::runtime_error("Unknown MIDI signal:\n" + std::string(buff)); // throw std::runtime_error("Unknown MIDI signal:\n" + std::string(buff));
printf("Unsupported signal, ignoring\n"); std::cerr << "Unsupported signal, ignoring\n" ;
return; return;
} }
@ -372,7 +363,7 @@ void Device::run_signal(char* buff)
+ ";channel=" + std::to_string(channel) + ";channel=" + std::to_string(channel)
+ ";velocity=" + std::to_string(value) + ";"; + ";velocity=" + std::to_string(value) + ";";
command += dequote(it.shell); command += dequote(it.shell);
std::thread(sh, command).detach(); std::thread(ztd::shr, command).detach();
} }
} }
} }
@ -399,7 +390,7 @@ void Device::run_signal(char* buff)
else else
command += std::to_string((long int) result); command += std::to_string((long int) result);
command += ";" + dequote(it.shell); command += ";" + dequote(it.shell);
std::thread(sh, command).detach(); std::thread(ztd::shr, command).detach();
} }
} }
} }
@ -425,7 +416,7 @@ void Device::run_signal(char* buff)
else else
command += std::to_string((long int) result); command += std::to_string((long int) result);
command += ";" + dequote(it.shell); command += ";" + dequote(it.shell);
std::thread(sh, command).detach(); std::thread(ztd::shr, command).detach();
} }
} }
} // if type } // if type
@ -447,7 +438,7 @@ void Device::loop(Device* dev)
for( auto it : dev->connectCommands ) for( auto it : dev->connectCommands )
{ {
std::thread(sh, dequote(it.shell)).detach(); std::thread(ztd::shr, dequote(it.shell)).detach();
} }
while (getline(&buff, &buff_size, stream) > 0) while (getline(&buff, &buff_size, stream) > 0)
@ -457,7 +448,7 @@ void Device::loop(Device* dev)
for( auto it : dev->disconnectCommands ) for( auto it : dev->disconnectCommands )
{ {
std::thread(sh, dequote(it.shell)).detach(); std::thread(ztd::shr, dequote(it.shell)).detach();
} }
log("Device '" + dev->name + "' disconnected\n"); log("Device '" + dev->name + "' disconnected\n");
@ -466,3 +457,12 @@ void Device::loop(Device* dev)
dev->thread_pid=-1; dev->thread_pid=-1;
free(buff); free(buff);
} }
void clean_devices()
{
for(auto it : device_list)
{
delete it;
}
device_list.clear();
}

View file

@ -71,8 +71,9 @@ ztd::chunkdat mimtochk_commands(const std::string& mim)
return chk; return chk;
} }
ztd::chunkdat mimtochk(const std::string& mim) ztd::chunkdat mimtochk(std::string mim)
{ {
mim = ztd::filedat::removeComments(mim);
ztd::chunkdat chk; ztd::chunkdat chk;
unsigned int i=0,j=0; unsigned int i=0,j=0;
_find_next(mim,i, "Device "); _find_next(mim,i, "Device ");
@ -97,6 +98,29 @@ ztd::chunkdat mimtochk(const std::string& mim)
return chk; return chk;
} }
std::string chktomim(ztd::chunkdat const& chk, std::string const& aligner)
{
std::string ret;
for(int i=0 ; i<chk.listSize() ; i++)
{
ztd::chunkdat& device=chk[i];
ret += "Device " + device.subChunkRef("name").str() + "\n\n";
for(int j=0 ; j < device["commands"].listSize() ; j++)
{
ztd::chunkdat command=device["commands"][j];
ret += aligner + "Command " + command["type"].str() + '\n';
command.erase("type");
for(auto it : command.getmap())
{
ret += aligner+aligner + it.first + '=' + it.second->str() + '\n';
}
ret += '\n';
// ret += command.str(2, aligner) + '\n';
}
}
return ret;
}
bool is_mim(const std::string& str) bool is_mim(const std::string& str)
{ {
unsigned int i=0; unsigned int i=0;

View file

@ -14,7 +14,7 @@
#include <ztd/options.hpp> #include <ztd/options.hpp>
#include <ztd/shell.hpp> #include <ztd/shell.hpp>
#define VERSION_STRING "v1.2" #define VERSION_STRING "v1.3.1a"
ztd::option_set options; ztd::option_set options;
@ -23,7 +23,6 @@ void help()
printf("zmidimap [options] <file>\n\nOptions:\n"); printf("zmidimap [options] <file>\n\nOptions:\n");
options.print_help(4, 25); options.print_help(4, 25);
printf("\n"); printf("\n");
printf("If piped, the map file will be read from standard input\n");
printf("See --mim-format --zfd-format --command-tags --shell-format options for details on map file format\n"); printf("See --mim-format --zfd-format --command-tags --shell-format options for details on map file format\n");
} }
@ -59,8 +58,7 @@ void cleanup()
void stop(int ret) void stop(int ret)
{ {
if(announce_thread_pid>0) kill_all();
kill(announce_thread_pid, SIGINT);
exit(ret); exit(ret);
} }
@ -69,155 +67,50 @@ void inthandler(int dummy)
stop(0); stop(0);
} }
int main(int argc, char* argv[]) void load_filedat(ztd::filedat& file, bool from_stdin, std::string const& path)
{ {
signal(SIGINT, inthandler); if(from_stdin)
signal(SIGCHLD, SIG_IGN); //not expecting returns from child processes
bool piped=false;
if (!isatty(fileno(stdin)))
piped = true;
options.add(ztd::option("\r [Help]"));
options.add(ztd::option('h',"help", false, "Display this help message"));
options.add(ztd::option('v',"version", false, "Display version"));
options.add(ztd::option("mim-format", false, "Display mim file format help"));
options.add(ztd::option("zfd-format", false, "Display zfd file format help"));
options.add(ztd::option("command-tags", false, "Display for command tag help"));
options.add(ztd::option("shell-format", false, "Display for shell format help"));
options.add(ztd::option("\r [Format]"));
options.add(ztd::option("no-log", false, "Disable console logging"));
options.add(ztd::option("\r [Devices]"));
options.add(ztd::option('l',"list", false, "List detected devices"));
options.add(ztd::option('L',"full-list", false, "Print whole device list details"));
options.add(ztd::option('p',"port", true, "Connect to device and output to console", "device"));
options.add(ztd::option("\r [Map file]"));
options.add(ztd::option('m',"mim", false, "Read file in mim format"));
options.add(ztd::option('z',"zfd", false, "Read file in zfd format"));
options.add(ztd::option('o',"output", true, "Output the resulting zfd map to file. - for stdout", "file"));
options.add(ztd::option("aligner", true, "String to use for aligning output map format. Default \\t", "string"));
options.add(ztd::option("\rIf no file format is specified, the program will try to guess the format"));
// options.add(ztd::option('i',"interactive", false, "Start in interactive mode"));
std::vector<std::string> arg;
try
{
arg = options.process(argc, argv);
}
catch(ztd::option_error& err)
{
printf("%s\n", err.what());
stop(1);
}
ztd::option* op=nullptr;
//exit options
if( options.find('h')->activated )
{
help();
stop(0);
}
if( options.find('v')->activated )
{
version();
stop(0);
}
if( options.find("mim-format")->activated )
{
printf("%s\n", MIM_FORMAT);
stop(0);
}
if( options.find("zfd-format")->activated )
{
printf("%s\n", ZFD_FORMAT);
stop(0);
}
if( options.find("command-tags")->activated )
{
printf("%s\n", COMMAND_TAGS);
stop(0);
}
if( options.find("shell-format")->activated )
{
printf("%s\n", SHELL_FORMAT);
stop(0);
}
if( options.find('L')->activated )
{
sh("aseqdump -l");
stop(0);
}
if( options.find('l')->activated )
{
sh(LIST_COMMAND);
stop(0);
}
op = options.find('p');
if( op->activated )
{
option_p(op->argument);
stop(0);
}
if (options.find('o')->activated)
{
log_on=false;
}
//behavioral options
if( options.find("no-log")->activated )
{
log_on=false;
}
std::string aligner="\t";
if(options.find("aligner")->activated)
{
aligner=options.find("aligner")->argument;
}
//no argument: display help
ztd::filedat file;
bool no_arg=false;
if (arg.size() <= 0 || arg[0] == "")
{
no_arg=true;
if(!piped)
{
help();
stop(0);
}
}
else
{
file.setFilePath(arg[0]);
}
//main processing
try
{
if(no_arg)
{ {
log("Loading map from stdin\n"); log("Loading map from stdin\n");
file.import_stdin(); file.setFilePath(path);
std::string str=file_strimport(path);
if(options["zfd"])
{
file.data() = str;
}
else if(options["mim"])
{
file.data() = mimtochk(str);
} }
else else
{ {
log("Loading map file '" + arg[0] + "'\n"); if(is_mim(str))
if(options.find("zfd")->activated) {
file.data() = mimtochk(str);
}
else
{
file.data() = str;
}
}
}
else
{
file.setFilePath(path);
log("Loading map file '" + path + "'\n");
if(options["zfd"])
{ {
file.import_file(); file.import_file();
} }
else if(options.find("mim")->activated) else if(options["mim"])
{ {
file.data() = mimtochk(file_strimport(arg[0])); file.data() = mimtochk(file_strimport(path));
} }
else else
{ {
std::string filestr=file_strimport(arg[0]); std::string filestr=file_strimport(path);
if(is_mim(filestr)) if(is_mim(filestr))
{ {
file.data() = mimtochk(filestr); file.data() = mimtochk(filestr);
@ -228,29 +121,209 @@ int main(int argc, char* argv[])
} }
} }
} }
if(options.find('o')->activated) }
void load_commands(ztd::chunkdat const& data)
{
clean_devices();
for(int i=0 ; i<data.listSize() ; i++)
{ {
if(options.find('o')->argument == "-") { Device *newDevice = new Device;
std::cout << file.strval(aligner) << std::endl; newDevice->import_chunk(data[i]);
device_list.push_back(newDevice);
log("Loaded "+std::to_string(newDevice->nb_command)+" commands for device '"+newDevice->name+"'\n");
}
}
int main(int argc, char* argv[])
{
signal(SIGINT, inthandler);
signal(SIGCHLD, SIG_IGN); //not expecting returns from child processes
bool autoreload=true;
bool from_stdin=false;
options.add(
ztd::option("\r [Help]"),
ztd::option('h',"help", false, "Display this help message"),
ztd::option('v',"version", false, "Display version"),
ztd::option("mim-format", false, "Display mim file format help"),
ztd::option("zfd-format", false, "Display zfd file format help"),
ztd::option("command-tags", false, "Display for command tag help"),
ztd::option("shell-format", false, "Display for shell format help"),
ztd::option("\r [Format]"),
ztd::option("no-log", false, "Disable console logging"),
ztd::option("\r [Devices]"),
ztd::option('l',"list", false, "List detected devices"),
ztd::option('L',"full-list", false, "Print whole device list details"),
ztd::option('p',"port", true, "Connect to device and output to console", "device"),
ztd::option("\r [Map file]"),
ztd::option('m',"mim", false, "Read file in mim format"),
ztd::option('z',"zfd", false, "Read file in zfd format"),
ztd::option('o',"output", true, "Output the resulting map to file", "file"),
ztd::option("out-zfd", false, "Output in zfd format"),
ztd::option("aligner", true, "String to use for aligning output map format. Default \\t", "string"),
ztd::option("no-reload", false, "Disable auto reloading when file changes are detected")
);
std::vector<std::string> arg;
try
{
arg = options.process(argc, argv);
}
catch(ztd::option_error& err)
{
printf("%s\n", err.what());
if(err.type() == ztd::option_error::unknown_option)
help();
stop(1);
}
//exit options
if( options['h'] )
{
help();
stop(0);
}
if( options['v'] )
{
version();
stop(0);
}
if( options["mim-format"] )
{
printf("%s\n", MIM_FORMAT);
stop(0);
}
if( options["zfd-format"] )
{
printf("%s\n", ZFD_FORMAT);
stop(0);
}
if( options["command-tags"] )
{
printf("%s\n", COMMAND_TAGS);
stop(0);
}
if( options["shell-format"] )
{
printf("%s\n", SHELL_FORMAT);
stop(0);
}
if( options['L'] )
{
ztd::shr("aseqdump -l");
stop(0);
}
if( options['l'] )
{
ztd::shr(LIST_COMMAND);
stop(0);
}
if( options['p'] )
{
option_p(options['p']);
stop(0);
}
if (options['o'])
{
log_on=false;
}
//behavioral options
if( options["no-reload"] )
{
autoreload=false;
}
if( options["no-log"] )
{
log_on=false;
}
std::string aligner="\t";
if(options["aligner"])
{
aligner=options["aligner"].argument;
}
//no argument: display help
ztd::filedat file;
std::string filepath;
if (arg.size() <= 0 || arg[0] == "")
{
help();
stop(0);
}
else
{
filepath=arg[0];
}
if(filepath == "-")
{
filepath = "/dev/stdin";
from_stdin = true;
}
//main processing
try
{
//load
load_filedat(file, from_stdin, filepath);
//output
if(options['o'])
{
std::string ret;
if(options["out-zfd"])
ret=file.data().str(0, aligner);
else
ret=chktomim(file.data(), aligner);
if(options['o'].argument == "-") {
std::cout << ret << std::endl;
} }
else { else {
file.setFilePath(options.find('o')->argument); std::ofstream output(options['o']);
file.export_file(); if(!output)
{
std::cerr << "Cannot write to file '" + options['o'].argument + "'\n";
return 1;
}
output << ret << std::endl ;
} }
return 0; return 0;
} }
//create commands //create commands
for(int i=0 ; i<file.data().listSize() ; i++) load_commands(file.data());
{
Device *newDevice = new Device;
newDevice->import_chunk(file[i]);
device_list.push_back(newDevice);
log("Loaded "+std::to_string(newDevice->nb_command)+" commands for device '"+newDevice->name+"'\n");
}
//main loop //main loop
log("Starting scan for devices\n"); log("Starting scan for devices\n");
announce_loop(); if(autoreload)
std::thread(filetime_loop, filepath).detach(); // start the killer thread
announce_loop(); // loop until killed
ztd::chunkdat bak_data = file.data();
while(autoreload)
{
log("Reloading file\n");
try
{
load_filedat(file, from_stdin, filepath);
load_commands(file.data());
bak_data = file.data();
}
catch (ztd::format_error& e)
{
ztd::printFormatException(e);
log("Reloading old config\n");
load_commands(bak_data);
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
log("Reloading old config\n");
load_commands(bak_data);
}
announce_loop(); // loop until killed
}
} }
catch (ztd::format_error& e) catch (ztd::format_error& e)
{ {

View file

@ -3,6 +3,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <string> #include <string>
#include <vector> #include <vector>
@ -13,6 +17,17 @@
int announce_thread_pid = 0; int announce_thread_pid = 0;
void kill_all()
{
if(announce_thread_pid>0)
kill(announce_thread_pid, SIGINT);
for(auto it : device_list)
{
if(it->thread_pid>0)
kill(it->thread_pid, SIGINT);
}
}
void device_check() void device_check()
{ {
char* buff = NULL; char* buff = NULL;
@ -86,3 +101,20 @@ void announce_loop()
if(buff != NULL) if(buff != NULL)
free(buff); free(buff);
} }
void filetime_loop(std::string const& filepath)
{
struct stat attrib;
stat(filepath.c_str(), &attrib);
time_t last_time=attrib.st_ctime;
while(true)
{
stat(filepath.c_str(), &attrib);
if(attrib.st_ctime > last_time)
{
kill_all();
last_time = attrib.st_ctime;
}
sleep(1);
}
}