Add Pitch bend support + some note fixes

This commit is contained in:
zawz 2019-06-16 14:47:55 +02:00
parent 1992f6f445
commit fd99dafc5f
6 changed files with 293 additions and 143 deletions

39
README
View file

@ -27,15 +27,22 @@ Format is a regular shell format
-- Environment -- Environment
- Global - Global
$channel: channel of the $channel: channel of the
$id: id of the note/controller
- Note - Note
$id: id of the note
$velocity: velocity of the note $velocity: velocity of the note
- Controller - Controller
$id: id of the controller
$value: value of the controller (remapped) $value: value of the controller (remapped)
$rawvalue: original value of the controller $rawvalue: original value of the controller
- Pitch bend
$value: value of the bend (remapped)
$rawvalue: original value of the bend
-- FILE FORMAT -- -- FILE FORMAT --
[<device>,<device>] [<device>,<device>]
@ -48,37 +55,39 @@ commands=[<command>,<command>]
- -
*name: string referring to client name of the device *name: string referring to client name of the device
- command format (global): - <command> format (global):
{ {
type=<note/controller> type=<note/controller/pitch>
id=<x>
shell=<shell command> shell=<shell command>
channel=<*/x> channel=<*/x>
} }
- -
*id: value from 0 to 127 referring to id of note/controller
> mandatory
*shell: shell command to be executed *shell: shell command to be executed
> mandatory > mandatory
*channel: value from 0 to 16 for channel. Can use * for any channel *channel: value from 0 to 16 for channel. Can use * for any channel
> optional, default * > optional, default *
- <command> format (note) - <command> format (note)
{ {
id=<x>
trigger=<x:y/x> trigger=<x:y/x>
} }
- -
*id: value from 0 to 127 referring to id of note/controller
> mandatory
*trigger: note velocity from 0 to 127 that triggers the command. Can enter an interval x:y or single value *trigger: note velocity from 0 to 127 that triggers the command. Can enter an interval x:y or single value
> optional, default 1:127 > optional, default 1:127
- <command> format (note) - <command> format (controller)
{ {
id=<x>
range=<x:y> range=<x:y>
remap=<x:y> remap=<x:y>
float=<true/false> float=<true/false>
} }
- -
*id: value from 0 to 127 referring to id of note/controller
> mandatory
*range: controller value from 0 to 127 that triggers command. Can enter an interval x:y or single value *range: controller value from 0 to 127 that triggers command. Can enter an interval x:y or single value
> optional, default 0:127 > optional, default 0:127
*remap: remaps the range to given interval. Interval can be inversed and float *remap: remaps the range to given interval. Interval can be inversed and float
@ -86,4 +95,18 @@ float=<true/false>
*float: boolean value defining if output is a floating point value *float: boolean value defining if output is a floating point value
> optional, default false > optional, default false
- <command> format (pitch)
{
range=<x:y>
remap=<x:y>
float=<true/false>
}
-
*range: controller value from -8192 to 8192 that triggers command. Can enter an interval x:y or single value
> optional, default -8192:8192
*remap: remaps the range to given interval. Interval can be inversed and float
> optional, default same as range
*float: boolean value defining if output is a floating point value
> optional, default false
- Comments can be written inside {} by doing //=<COMMENT> - Comments can be written inside {} by doing //=<COMMENT>

View file

@ -6,14 +6,11 @@
{ {
type=note type=note
id=9 id=9
channel=0
trigger=1:127
shell=echo "Note $id on ch$channel v$velocity" shell=echo "Note $id on ch$channel v$velocity"
}, },
{ {
type=note type=note
id=9 id=9
channel=0
trigger=0 trigger=0
shell=echo "Note $id off ch$channel" shell=echo "Note $id off ch$channel"
}, },

View file

@ -9,10 +9,10 @@ class NoteCommand
{ {
public: public:
NoteCommand(uint8_t i, uint8_t ch, uint8_t l, uint8_t h, std::string sh); NoteCommand(uint8_t i, int8_t ch, uint8_t l, uint8_t h, std::string sh);
uint8_t id; uint8_t id;
uint8_t channel; int8_t channel;
uint8_t low; uint8_t low;
uint8_t high; uint8_t high;
std::string shell; std::string shell;
@ -34,5 +34,20 @@ public:
std::string shell; std::string shell;
}; };
class PitchCommand
{
public:
PitchCommand(uint8_t ch, int16_t l, int16_t h, float ml, float mh, bool fl, std::string sh);
int8_t channel;
int16_t min;
int16_t max;
float mapMin;
float mapMax;
bool floating;
std::string shell;
};
#endif //COMMAND_HPP #endif //COMMAND_HPP

View file

@ -16,6 +16,7 @@ public:
virtual ~Device(); virtual ~Device();
bool start_loop(); bool start_loop();
void run_signal(char* buff);
bool import_chunk(Chunk const& ch); bool import_chunk(Chunk const& ch);
Chunk export_chunk(); Chunk export_chunk();
@ -26,7 +27,8 @@ public:
uint32_t nb_command; uint32_t nb_command;
std::vector<NoteCommand> noteCommands[128]; std::vector<NoteCommand> noteCommands[128];
std::vector<ControllerCommand> ctrlCommands[128]; std::vector<ControllerCommand> ctrlCommands[128];
// std::vector<Command> systCommands; std::vector<PitchCommand> pitchCommands;
// std::vector<Command> sysCommands;
std::thread thread; std::thread thread;
private: private:

View file

@ -1,6 +1,6 @@
#include "command.hpp" #include "command.hpp"
NoteCommand::NoteCommand(uint8_t i, uint8_t ch, uint8_t l, uint8_t h, std::string sh) NoteCommand::NoteCommand(uint8_t i, int8_t ch, uint8_t l, uint8_t h, std::string sh)
{ {
this->id=i; this->id=i;
this->channel=ch; this->channel=ch;
@ -20,3 +20,14 @@ ControllerCommand::ControllerCommand(uint8_t i, int8_t ch, uint8_t l, uint8_t h,
this->floating=fl; this->floating=fl;
this->shell=sh; this->shell=sh;
} }
PitchCommand::PitchCommand(uint8_t ch, int16_t l, int16_t h, float ml, float mh, bool fl, std::string sh)
{
this->channel=ch;
this->min=l;
this->max=h;
this->mapMin=ml;
this->mapMax=mh;
this->floating=fl;
this->shell=sh;
}

View file

@ -54,12 +54,8 @@ bool Device::import_chunk(Chunk const& ch)
else else
{ {
int8_t channel; int8_t channel;
uint8_t id;
std::string shell; std::string shell;
//id
id=stoi(tch["id"].strval());
//channel //channel
if(tch.subChunkPtr("channel") == nullptr || tch["channel"].strval()=="*") if(tch.subChunkPtr("channel") == nullptr || tch["channel"].strval()=="*")
channel=-1; channel=-1;
@ -72,37 +68,50 @@ bool Device::import_chunk(Chunk const& ch)
//type //type
if(tstr == "note") //type note if(tstr == "note") //type note
{ {
uint8_t low; uint8_t id;
uint8_t high; uint8_t low=1;
uint8_t high=127;
std::string tt; std::string tt;
//id
id=stoi(tch["id"].strval());
//trigger //trigger
tt=tch["trigger"].strval(); Chunk* ttch=tch.subChunkPtr("trigger");
auto tpos=tt.find(':'); if(ttch != nullptr)
if(tpos == std::string::npos)
{ {
//single value tt=ttch->strval();
low=stoi(tt); auto tpos=tt.find(':');
high=low; if(tpos == std::string::npos)
} {
else //single value
{ low=stoi(tt);
//range high=low;
low=stoi(tt.substr(0,tpos)); }
tpos++; else
high=stoi(tt.substr(tpos, tt.size()-tpos)); {
//range
low=stoi(tt.substr(0,tpos));
tpos++;
high=stoi(tt.substr(tpos, tt.size()-tpos));
}
} }
printf("%d:%d\n",low,high);
this->noteCommands[id].push_back(NoteCommand(id,channel,low,high,shell)); this->noteCommands[id].push_back(NoteCommand(id,channel,low,high,shell));
this->nb_command++; this->nb_command++;
} }
else if(tstr == "controller") //type controller else if(tstr == "controller") //type controller
{ {
uint8_t id;
uint8_t min=0; uint8_t min=0;
uint8_t max=127; uint8_t max=127;
float mapMin; float mapMin;
float mapMax; float mapMax;
bool floating=false; bool floating=false;
//id
id=stoi(tch["id"].strval());
//range //range
Chunk* ttch=tch.subChunkPtr("range"); Chunk* ttch=tch.subChunkPtr("range");
if(ttch != nullptr) if(ttch != nullptr)
@ -152,6 +161,63 @@ bool Device::import_chunk(Chunk const& ch)
this->ctrlCommands[id].push_back(ControllerCommand(id,channel,min,max,mapMin,mapMax,floating,shell)); this->ctrlCommands[id].push_back(ControllerCommand(id,channel,min,max,mapMin,mapMax,floating,shell));
this->nb_command++; this->nb_command++;
} }
else if(tstr == "pitch") //type pitch bend
{
int16_t min=-8192;
int16_t max=8192;
float mapMin;
float mapMax;
bool floating=false;
//range
Chunk* ttch=tch.subChunkPtr("range");
if(ttch != nullptr)
{
std::string range=ttch->strval();
auto tpos=range.find(':');
if(tpos == std::string::npos)
{
//single value
min=stoi(range);
max=min;
}
else
{
//range
min=stoi(range.substr(0,tpos));
tpos++;
max=stoi(range.substr(tpos, range.size()-tpos));
}
}
//remap
ttch=tch.subChunkPtr("remap");
if(ttch != nullptr)
{
std::string map=ttch->strval();
auto tpos=map.find(':');
mapMin=stof(map.substr(0,tpos));
tpos++;
mapMax=stof(map.substr(tpos, map.size()-tpos));
}
//floating
ttch=tch.subChunkPtr("float");
if(ttch != nullptr)
{
std::string tfl=ttch->strval();
if( tfl == "true" || tfl == "yes" || tfl == "y")
floating=true;
}
else
{
mapMin=min;
mapMax=max;
}
this->pitchCommands.push_back(PitchCommand(channel,min,max,mapMin,mapMax,floating,shell));
this->nb_command++;
}
else else
{ {
throw std::runtime_error("Command type " + tstr + " doesn't exist"); throw std::runtime_error("Command type " + tstr + " doesn't exist");
@ -162,6 +228,150 @@ bool Device::import_chunk(Chunk const& ch)
return true; return true;
} }
void Device::run_signal(char* buff)
{
if ( (strstr(buff, "Port unsubscribed") != NULL) ) // distonnected
{
std::string kill_command=KILL_COMMAND_FH + this->name + KILL_COMMAND_SH;
system(kill_command.c_str()); // kill the process
this->busy=false;
}
else if (index(buff, ':') != NULL) // MIDI command
{
if (strstr(buff, "System exclusive") != NULL)
{
printf("%s", buff);
//do stuff
}
else
{
int t;
char type;
int8_t channel;
int ctid;
int16_t value;
char* pos=NULL;
bool note_off=false;
//note off special case
if (strstr(buff,"Note off") != NULL)
note_off=true;
// type read
if (strstr(buff,"note") != NULL)
type='n';
else if (strstr(buff,"controller") != NULL)
type='c';
else if (strstr(buff,"Pitch") != NULL)
type='p';
else
{
throw std::runtime_error("Unknown MIDI signal\n");
return;
}
//channel read
pos=index(buff, ',');
t=1;
while (*(pos-t-1) != ' ')
t++;
channel = std::stoi( std::string(pos-t, t) );
pos+=2;
//ctid read (only note anc controller)
if(type=='n' || type=='c')
{
while (*(pos) != ' ')
pos++;
pos++;
t=1;
while ( isNum(*(pos+t)) )
t++;
ctid = std::stoi( std::string(pos, t) );
pos+=t+2;
}
//value read
if(!note_off)
{
while (*(pos) != ' ')
pos++;
pos++;
t=1;
while ( isNum(*(pos+t)) )
t++;
value = std::stoi( std::string(pos, t));
}
else
value=0;
if (type == 'n')
{
for( auto it : this->noteCommands[ctid])
{
if((it.channel == -1 || it.channel == channel) && it.low <= value && it.high >= value)
{
std::string command="id=" + std::to_string(ctid) + ";channel=" + std::to_string(channel) + ";velocity=" + std::to_string(value) + ";";
command += it.shell;
std::thread(sh, command).detach();
}
}
}
else if(type == 'c')
{
for( auto it : this->ctrlCommands[ctid])
{
if((it.channel == -1 || it.channel == channel) && it.min <= value && it.max >= value)
{
//remapping of value
float result;
if(it.min == it.max)
result = it.mapMin;
else
result=(value-it.min)*(it.mapMax-it.mapMin)/(it.max-it.min)+it.mapMin;
//command execution
std::string command="id=" + std::to_string(ctid) + ";channel=" + std::to_string(channel) + ";rawvalue=" + std::to_string(value) + ";value=";
if(it.floating)
command += std::to_string(result);
else
command += std::to_string((long int) result);
command += ";" + it.shell;
std::thread(sh, command).detach();
}
}
}
else if(type == 'p')
{
for( auto it : this->pitchCommands)
{
if((it.channel == -1 || it.channel == channel) && it.min <= value && it.max >= value)
{
//remapping of value
float result;
if(it.min == it.max)
result = it.mapMin;
else
result=(value-it.min)*(it.mapMax-it.mapMin)/(it.max-it.min)+it.mapMin;
//command execution
std::string command="id=" + std::to_string(ctid) + ";channel=" + std::to_string(channel) + ";rawvalue=" + std::to_string(value) + ";value=";
if(it.floating)
command += std::to_string(result);
else
command += std::to_string((long int) result);
command += ";" + it.shell;
std::thread(sh, command).detach();
}
}
} // if type
} //if system exclusive
} //while
}
void Device::loop(Device* dev) void Device::loop(Device* dev)
{ {
std::string command = "aseqdump -p '" + dev->name + '\''; std::string command = "aseqdump -p '" + dev->name + '\'';
@ -171,115 +381,7 @@ void Device::loop(Device* dev)
while (getline(&buff, &buff_size, stream) > 0) while (getline(&buff, &buff_size, stream) > 0)
{ {
if ( (strstr(buff, "Port unsubscribed") != NULL) ) // distonnected dev->run_signal(buff);
{
std::string kill_command=KILL_COMMAND_FH + dev->name + KILL_COMMAND_SH;
system(kill_command.c_str()); // kill the process
dev->busy=false;
}
else if (index(buff, ':') != NULL) // MIDI command
{
if (strstr(buff, "System exclusive") != NULL)
{
printf("%s", buff);
//do stuff
}
else
{
int t;
char type;
int8_t channel;
int ctid;
uint8_t value;
char* pos=NULL;
bool note_off=false;
if (strstr(buff,"Note off") != NULL)
note_off=true;
//channel read
pos=index(buff, ',');
t=1;
while (*(pos-t-1) != ' ')
t++;
channel = std::stoi( std::string(pos-t, t) );
pos+=2;
//type
if (*pos == 'c') //controller signal
type='c';
else if (*pos == 'n') //note signal
type='n';
else
{
throw std::runtime_error("Unknown MIDI signal\n");
return;
}
//ctid read
while (*(pos) != ' ')
pos++;
pos++;
t=1;
while ( isNum(*(pos+t)) )
t++;
ctid = std::stoi( std::string(pos, t) );
pos+=t+2;
//value read
if(!note_off)
{
while (*(pos) != ' ')
pos++;
pos++;
t=1;
while ( isNum(*(pos+t)) )
t++;
value = std::stoi( std::string(pos, t));
}
else
value=0;
if (type == 'n')
{
for( auto it : dev->noteCommands[ctid])
{
if((it.channel == -1 || it.channel == channel) && it.low <= value && it.high >= value)
{
std::string command="id=" + std::to_string(ctid) + ";channel=" + std::to_string(channel) + ";velocity=" + std::to_string(value) + ";";
command += it.shell;
std::thread(sh, command).detach();
}
}
}
if(type == 'c')
{
for( auto it : dev->ctrlCommands[ctid])
{
if((it.channel == -1 || it.channel == channel) && it.min <= value && it.max >= value)
{
//remapping of value
float result;
if(it.min == it.max)
result = it.mapMin;
else
result=(value-it.min)*(it.mapMax-it.mapMin)/(it.max-it.min)+it.mapMin;
//command execution
std::string command="id=" + std::to_string(ctid) + ";channel=" + std::to_string(channel) + ";rawvalue=" + std::to_string(value) + ";value=";
if(it.floating)
command += std::to_string(result);
else
command += std::to_string((long int) result);
command += ";" + it.shell;
std::thread(sh, command).detach();
}
}
}
}
}
} }
printf("Device '%s' disconnected\n", dev->name.c_str()); printf("Device '%s' disconnected\n", dev->name.c_str());