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
- Global
$channel: channel of the
$id: id of the note/controller
- Note
$id: id of the note
$velocity: velocity of the note
- Controller
$id: id of the controller
$value: value of the controller (remapped)
$rawvalue: original value of the controller
- Pitch bend
$value: value of the bend (remapped)
$rawvalue: original value of the bend
-- FILE FORMAT --
[<device>,<device>]
@ -48,37 +55,39 @@ commands=[<command>,<command>]
-
*name: string referring to client name of the device
- command format (global):
- <command> format (global):
{
type=<note/controller>
id=<x>
type=<note/controller/pitch>
shell=<shell command>
channel=<*/x>
}
-
*id: value from 0 to 127 referring to id of note/controller
> mandatory
*shell: shell command to be executed
> mandatory
*channel: value from 0 to 16 for channel. Can use * for any channel
> optional, default *
- <command> format (note)
{
id=<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
> optional, default 1:127
- <command> format (note)
- <command> format (controller)
{
id=<x>
range=<x:y>
remap=<x:y>
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
> optional, default 0:127
*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
> 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>

View file

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

View file

@ -9,10 +9,10 @@ class NoteCommand
{
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 channel;
int8_t channel;
uint8_t low;
uint8_t high;
std::string shell;
@ -34,5 +34,20 @@ public:
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

View file

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

View file

@ -1,6 +1,6 @@
#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->channel=ch;
@ -20,3 +20,14 @@ ControllerCommand::ControllerCommand(uint8_t i, int8_t ch, uint8_t l, uint8_t h,
this->floating=fl;
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
{
int8_t channel;
uint8_t id;
std::string shell;
//id
id=stoi(tch["id"].strval());
//channel
if(tch.subChunkPtr("channel") == nullptr || tch["channel"].strval()=="*")
channel=-1;
@ -72,12 +68,19 @@ bool Device::import_chunk(Chunk const& ch)
//type
if(tstr == "note") //type note
{
uint8_t low;
uint8_t high;
uint8_t id;
uint8_t low=1;
uint8_t high=127;
std::string tt;
//id
id=stoi(tch["id"].strval());
//trigger
tt=tch["trigger"].strval();
Chunk* ttch=tch.subChunkPtr("trigger");
if(ttch != nullptr)
{
tt=ttch->strval();
auto tpos=tt.find(':');
if(tpos == std::string::npos)
{
@ -92,17 +95,23 @@ bool Device::import_chunk(Chunk const& ch)
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->nb_command++;
}
else if(tstr == "controller") //type controller
{
uint8_t id;
uint8_t min=0;
uint8_t max=127;
float mapMin;
float mapMax;
bool floating=false;
//id
id=stoi(tch["id"].strval());
//range
Chunk* ttch=tch.subChunkPtr("range");
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->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
{
throw std::runtime_error("Command type " + tstr + " doesn't exist");
@ -162,20 +228,13 @@ bool Device::import_chunk(Chunk const& ch)
return true;
}
void Device::loop(Device* dev)
void Device::run_signal(char* buff)
{
std::string command = "aseqdump -p '" + dev->name + '\'';
FILE *stream = popen(command.c_str(), "r");
char* buff = NULL;
size_t buff_size = 0;
while (getline(&buff, &buff_size, stream) > 0)
{
if ( (strstr(buff, "Port unsubscribed") != NULL) ) // distonnected
{
std::string kill_command=KILL_COMMAND_FH + dev->name + KILL_COMMAND_SH;
std::string kill_command=KILL_COMMAND_FH + this->name + KILL_COMMAND_SH;
system(kill_command.c_str()); // kill the process
dev->busy=false;
this->busy=false;
}
else if (index(buff, ':') != NULL) // MIDI command
@ -191,13 +250,27 @@ void Device::loop(Device* dev)
char type;
int8_t channel;
int ctid;
uint8_t value;
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;
@ -206,18 +279,9 @@ void Device::loop(Device* dev)
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
//ctid read (only note anc controller)
if(type=='n' || type=='c')
{
throw std::runtime_error("Unknown MIDI signal\n");
return;
}
//ctid read
while (*(pos) != ' ')
pos++;
pos++;
@ -226,7 +290,7 @@ void Device::loop(Device* dev)
t++;
ctid = std::stoi( std::string(pos, t) );
pos+=t+2;
}
//value read
if(!note_off)
@ -244,7 +308,7 @@ void Device::loop(Device* dev)
if (type == 'n')
{
for( auto it : dev->noteCommands[ctid])
for( auto it : this->noteCommands[ctid])
{
if((it.channel == -1 || it.channel == channel) && it.low <= value && it.high >= value)
{
@ -254,9 +318,9 @@ void Device::loop(Device* dev)
}
}
}
if(type == 'c')
else if(type == 'c')
{
for( auto it : dev->ctrlCommands[ctid])
for( auto it : this->ctrlCommands[ctid])
{
if((it.channel == -1 || it.channel == channel) && it.min <= value && it.max >= value)
{
@ -278,8 +342,46 @@ void Device::loop(Device* dev)
}
}
}
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)
{
std::string command = "aseqdump -p '" + dev->name + '\'';
FILE *stream = popen(command.c_str(), "r");
char* buff = NULL;
size_t buff_size = 0;
while (getline(&buff, &buff_size, stream) > 0)
{
dev->run_signal(buff);
}
printf("Device '%s' disconnected\n", dev->name.c_str());