+Added support for new easier map file format
+Added version print
+Added map output options
*Reorganized options
*Internal fixes on quotes
This commit is contained in:
zawz 2019-08-17 01:35:49 +02:00
parent 8b48ebf39f
commit 8d3d8f8ceb
12 changed files with 414 additions and 153 deletions

View file

@ -1,78 +1,49 @@
[
// LAUNCH CONTROL
{
name=Launch Control
commands=[
// KNOBS HIGH
{
//KNOB HA
//displays value 0:127 for knobs 21-28
type=controller
id=21:28
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
channel=*
shell=echo "Knob #$id ch$channel:$value"
},
{
//KNOB L2
//displays value -100:100 for knob 42 on channel 0
type=controller
id=42
channel=0
remap=-100:100
shell=echo "Knob #$id ch$channel:$value r:$rawvalue"
},
{
//KNOB L3 H1
//displays value 0:1:0 for knob 42 on channel 1 (first half)
type=controller
id=42
channel=1
range=0:63
remap=0:1
float=true
shell=echo "Knob #$id ch$channel:$value"
},
{
//KNOB L3 H2
//displays value 0:1:0 for knob 42 on channel 1 (second half)
type=controller
id=42
channel=1
range=64:127
remap=1:0
float=true
shell=echo "Knob #$id ch$channel:$value"
}
]
}
,
// LAUNCHPAD
{
name=Launchpad S
commands=[
{
// ANY NOTE ON
type=note
id=*
shell=echo "Note $id on velocity:$velocity"
},
{
// ANY NOTE OFF
type=note
id=*
trigger=0
shell=echo "Note $id off"
}
]
}
]
Device "Launch Control"
//display value 0:127 for knobs 21-28
Command controller
id=21:28
shell=echo "Knob #$id ch$channel:$value"
//display value 0:127 for knob 41 from any channel
Command controller
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
remap=-100:100
shell=echo "Knob #$id ch0:$value r:$rawvalue"
//display value 0:1:0 for knob 42 on channel 1 (first half)
Command controller
id=42
channel=1
range=0:63
remap=0:1
float=true
shell=echo "Knob #$id ch1:$value r:$rawvalue"
//display value 0:1:0 for knob 42 on channel 1 (second half)
Command controller
id=42
channel=1
range=64:127
remap=1:0
float=true
shell=echo "Knob #$id ch1:$value r:$rawvalue"
Device "Launchpad S"
Command note
shell=echo "Note $id on velocity:$velocity"
Command note
trigger=0
shell=echo "Note $id off"

75
example.zfd Normal file
View file

@ -0,0 +1,75 @@
[
// LAUNCH CONTROL
{
name=Launch Control
commands=[
// KNOBS HIGH
{
//KNOB HA
//displays value 0:127 for knobs 21-28
type=controller
id=21:28
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
//displays value -100:100 for knob 42 on channel 0
type=controller
id=42
channel=0
remap=-100:100
shell=echo "Knob #$id ch0:$value r:$rawvalue"
},
{
//KNOB L3 H1
//displays value 0:1:0 for knob 42 on channel 1 (first half)
type=controller
id=42
channel=1
range=0:63
remap=0:1
float=true
shell=echo "Knob #$id ch1:$value r:$rawvalue"
},
{
//KNOB L3 H2
//displays value 0:1:0 for knob 42 on channel 1 (second half)
type=controller
id=42
channel=1
range=64:127
remap=1:0
float=true
shell=echo "Knob #$id ch1:$value r:$rawvalue"
}
]
}
,
// LAUNCHPAD
{
name=Launchpad S
commands=[
{
// ANY NOTE ON
type=note
shell=echo "Note $id on velocity:$velocity"
},
{
// ANY NOTE OFF
type=note
trigger=0
shell=echo "Note $id off"
}
]
}
]

View file

@ -3,7 +3,8 @@ IDIR=include
cp $FORMAT_FOLDER/help_template_head $IDIR/help.h
echo "#define FILE_FORMAT \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/file-format)\"" >> $IDIR/help.h
echo "#define ZFD_FORMAT \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/zfd-format)\"" >> $IDIR/help.h
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
echo "#define COMMAND_TAGS \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/command-tags)\"" >> $IDIR/help.h

View file

@ -1,14 +1,5 @@
-- [COMMAND TAGS] --
[Global tags]
type=<type>
shell=<shell command>
--
*type: type of the signal: note/controller/pitch/system/connect/disconnect
> mandatory
*shell: shell command to be executed
> mandatory
[Note tags]
id=<interval>
channel=<x/*>

View file

@ -1,23 +0,0 @@
-- [FILE FORMAT] --
[<device>,<device>]
<device> format
{
name=<name>
commands=[<command>,<command>]
}
--
*name: string referring to client name of the device
<command> format
{
<tag>=<value>
<tag>=<value>
...
}
--
value can be concatenated with \"\" or ''
see command-tags
Full info on file format: https://github.com/zawwz/zFiledat

22
help_format/mim-format Normal file
View file

@ -0,0 +1,22 @@
-- [MIM FILE FORMAT] --
Device <name>
Command <type>
shell=<shell command>
<tag>=<value>
<tag>=<value>
...
Device <name>
...
--
*name: string referring to client name of the device
> mandatory
*type: type of the signal: note/controller/pitch/system/connect/disconnect
> mandatory
*shell: shell command to be executed
> mandatory
Shell command can be concatenated with \"\" or ''
see --command-tags for optional command tags

28
help_format/zfd-format Normal file
View file

@ -0,0 +1,28 @@
-- [ZFD FILE FORMAT] --
[<device>,<device>]
-- <device> format --
{
name=<name>
commands=[<command>,<command>]
}
--
*name: string referring to client name of the device
<command> format
{
type=<type>
shell=<shell command>
<tag>=<value>
<tag>=<value>
...
}
--
*type: type of the signal: note/controller/pitch/system/connect/disconnect
> mandatory
*shell: shell command to be executed
> mandatory
Shell command can be concatenated with \"\" or ''
see --command-tags for optional command tags

12
include/format.hpp Normal file
View file

@ -0,0 +1,12 @@
#ifndef FORMAT_HPP
#define FORMAT_HPP
#include <ztd/filedat.hpp>
ztd::chunkdat mimtochk(const std::string& mim);
std::string file_strimport(const std::string& path);
bool is_mim(const std::string& str);
#endif //FORMAT_HPP

View file

@ -1,6 +1,7 @@
#ifndef HELP_H
#define HELP_H
#define FILE_FORMAT "zmidimap\n-- [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 <tag>=<value>\n <tag>=<value>\n ...\n }\n--\n value can be concatenated with \"\" or ''\n see command-tags\n\nFull info on file format: https://github.com/zawwz/zFiledat"
#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 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 COMMAND_TAGS "zmidimap\n-- [COMMAND TAGS] --\n\n[Global tags]\n type=<type>\n shell=<shell command>\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\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 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"
#endif //HELP_H

View file

@ -11,6 +11,30 @@
std::vector<Device*> device_list;
static std::string dequote(const std::string& in)
{
std::string ret;
const char quote = in[0];
if(quote != '\'' && quote !='\"')
return in;
unsigned int i=1;
while(i<in.size())
{
if(in[i] == quote) {
break;
}
else if(i+1 < in.size() && in[i] == '\\' && in[i+1] == quote) {
i+=2;
ret += quote;
}
else{
ret += in[i];
i++;
}
}
return ret;
}
static bool _isNum(char a)
{
return (a>='0' && a<='9');
@ -111,7 +135,7 @@ bool importBool(ztd::chunkdat const& ch, std::string const& tag, bool defbool)
bool Device::import_chunk(ztd::chunkdat const& ch)
{
ztd::chunkdat& cch = ch["commands"];
this->name=ch["name"].strval();
this->name=dequote(ch["name"].strval());
for(int i=0 ; i<cch.listSize() ; i++)
{
ztd::chunkdat& tch=cch[i];
@ -270,7 +294,7 @@ void Device::run_signal(char* buff)
for( auto it : this->sysCommands )
{
std::string command="code=" + strval + ";";
command += it.shell;
command += dequote(it.shell);
std::thread(sh, command).detach();
}
}
@ -347,7 +371,7 @@ void Device::run_signal(char* buff)
std::string command="id=" + std::to_string(ctid)
+ ";channel=" + std::to_string(channel)
+ ";velocity=" + std::to_string(value) + ";";
command += it.shell;
command += dequote(it.shell);
std::thread(sh, command).detach();
}
}
@ -374,7 +398,7 @@ void Device::run_signal(char* buff)
command += std::to_string(result);
else
command += std::to_string((long int) result);
command += ";" + it.shell;
command += ";" + dequote(it.shell);
std::thread(sh, command).detach();
}
}
@ -400,7 +424,7 @@ void Device::run_signal(char* buff)
command += std::to_string(result);
else
command += std::to_string((long int) result);
command += ";" + it.shell;
command += ";" + dequote(it.shell);
std::thread(sh, command).detach();
}
}
@ -423,7 +447,7 @@ void Device::loop(Device* dev)
for( auto it : dev->connectCommands )
{
std::thread(sh, it.shell).detach();
std::thread(sh, dequote(it.shell)).detach();
}
while (getline(&buff, &buff_size, stream) > 0)
@ -433,7 +457,7 @@ void Device::loop(Device* dev)
for( auto it : dev->disconnectCommands )
{
std::thread(sh, it.shell).detach();
std::thread(sh, dequote(it.shell)).detach();
}
log("Device '" + dev->name + "' disconnected\n");

105
src/format.cpp Normal file
View file

@ -0,0 +1,105 @@
#include "format.hpp"
static void _skipline(const std::string& mim, unsigned int& i)
{
while(i<mim.size() && mim[i] != '\n') {
i++;
}
}
static void _skipread(const std::string& mim, unsigned int& i)
{
while(i<mim.size())
{
if(i+1<mim.size() && mim[i] == '/' && mim[i+1] == '/') {
_skipline(mim, i);
}
if(ztd::filedat::isRead(mim[i])) {
break;
}
i++;
}
}
static void _find_next(const std::string& mim, unsigned int& i, const std::string& str)
{
while(i<mim.size())
{
_skipread(mim, i);
if(i+str.size() < mim.size() && mim.substr(i, str.size()) == str)
{
break;
}
else
{
_skipline(mim, i);
}
}
}
std::string file_strimport(const std::string& path)
{
std::ifstream file(path);
std::string ret, line;
while(file)
{
getline(file, line);
ret += line + '\n';
}
return ret;
}
ztd::chunkdat mimtochk_commands(const std::string& mim)
{
ztd::chunkdat chk;
unsigned int i=0,j=0;
_find_next(mim,i, "Command ");
while(i<mim.size())
{
i+=7;
j=i;
_skipline(mim,j);
std::string type=mim.substr(i,j-i);
j++;
i=j;
_find_next(mim,j, "Command ");
std::string flags=mim.substr(i,j-i);
i=j;
chk.add("{type=" + type + '\n' + flags + '}');
}
return chk;
}
ztd::chunkdat mimtochk(const std::string& mim)
{
ztd::chunkdat chk;
unsigned int i=0,j=0;
_find_next(mim,i, "Device ");
while(i<mim.size())
{
ztd::chunkdat device;
i+=7;
j=i;
_skipline(mim,j);
std::string name=mim.substr(i,j-i);
j++;
i=j;
_find_next(mim,j, "Device ");
std::string commands=mim.substr(i,j-i);
i=j;
device.add("name", name);
device.add("commands", mimtochk_commands(commands));
chk.add(device);
}
return chk;
}
bool is_mim(const std::string& str)
{
unsigned int i=0;
_skipread(str, i);
return str.substr(i,7) == "Device ";
}

View file

@ -8,17 +8,26 @@
#include "log.hpp"
#include "help.h"
#include "format.hpp"
#include <ztd/filedat.hpp>
#include <ztd/options.hpp>
#include <ztd/shell.hpp>
#define VERSION_STRING "v1.2"
ztd::option_set options;
void help()
{
printf("zmidimap [options] <midimap file>\n\nOptions:\n");
options.print_help(2, 25);
printf("\nSee --file-format --command-tags --shell-format options for details on map file format\n");
printf("zmidimap [options] <file>\n\nOptions:\n");
options.print_help(4, 25);
printf("\nSee --zfd-format --command-tags --shell-format options for details on map file format\n");
}
void version()
{
printf("zmidimap %s\n", VERSION_STRING);
}
void option_p(const std::string& port)
@ -67,14 +76,27 @@ int main(int argc, char* argv[])
if (!isatty(fileno(stdin)))
piped = true;
options.add(ztd::option('h',"help", false, "Display this help message"));
options.add(ztd::option("file-format", false, "Display 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('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("no-log", false, "Disable console logging"));
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('o',"output", true, "Output the resulting zfd map to file. - for stdout"));
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("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;
@ -84,54 +106,53 @@ int main(int argc, char* argv[])
}
catch(ztd::option_error& err)
{
printf("Option error: %s\n", err.what());
printf("%s\n", err.what());
stop(1);
}
//exit options
ztd::option* op=nullptr;
op = options.find('h');
if( op->activated )
//exit options
if( options.find('h')->activated )
{
help();
stop(0);
}
op = options.find("file-format");
if( op->activated )
if( options.find('v')->activated )
{
printf("%s\n", FILE_FORMAT);
version();
stop(0);
}
op = options.find("command-tags");
if( op->activated )
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);
}
op = options.find("shell-format");
if( op->activated )
if( options.find("shell-format")->activated )
{
printf("%s\n", SHELL_FORMAT);
stop(0);
}
op = options.find('h');
if( op->activated )
{
help();
stop(0);
}
op = options.find('L');
if( op->activated )
if( options.find('L')->activated )
{
sh("aseqdump -l");
stop(0);
}
op = options.find('l');
if( op->activated )
if( options.find('l')->activated )
{
sh(LIST_COMMAND);
stop(0);
}
op = options.find('p');
if( op->activated )
{
@ -140,11 +161,15 @@ int main(int argc, char* argv[])
}
//behavioral options
op = options.find("no-log");
if( op->activated )
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;
@ -175,10 +200,39 @@ int main(int argc, char* argv[])
else
{
log("Loading map file '" + arg[0] + "'\n");
file.import_file();
if(options.find("zfd")->activated)
{
file.import_file();
}
else if(options.find("mim")->activated)
{
file.data() = mimtochk(file_strimport(arg[0]));
}
else
{
std::string filestr=file_strimport(arg[0]);
if(is_mim(filestr))
{
file.data() = mimtochk(filestr);
}
else
{
file.import_file();
}
}
}
if(options.find('o')->activated)
{
if(options.find('o')->argument == "-") {
std::cout << file.strval(aligner) << std::endl;
}
else {
}
return 0;
}
//create commands
// potential parallel improvement
for(int i=0 ; i<file.data().listSize() ; i++)
{
Device *newDevice = new Device;