1.0 release
+Add options +Add help +Add support for multiple identical devices *New README *Bug fixes
This commit is contained in:
parent
09c1f1386a
commit
43502018c5
18 changed files with 623 additions and 344 deletions
128
README
128
README
|
|
@ -1,128 +0,0 @@
|
||||||
|
|
||||||
Maps midi signals coming from ALSA midi devices to shell commands
|
|
||||||
|
|
||||||
Build: `$ make && sudo make install`
|
|
||||||
|
|
||||||
usage: midiMap <config file>
|
|
||||||
This is a daemon program, it does not start any background process by itself and needs to be constantly running for the mapping to be active
|
|
||||||
|
|
||||||
This program is in early stage but is fully functional without any major errors
|
|
||||||
|
|
||||||
Known issues:
|
|
||||||
- Doesn't support multiple identical devices
|
|
||||||
|
|
||||||
|
|
||||||
See 'example.mim' for an example config file
|
|
||||||
|
|
||||||
To scan for devices use `$ aseqdump -l`
|
|
||||||
To scan a device's inputs use `$ aseqdump -p <client name>`
|
|
||||||
|
|
||||||
-- [COMMAND FORMAT] --
|
|
||||||
|
|
||||||
Format is regular shell format
|
|
||||||
|
|
||||||
-- Environment
|
|
||||||
|
|
||||||
[Note]
|
|
||||||
$id: id of the note
|
|
||||||
$channel: channel of the note
|
|
||||||
$velocity: velocity of the note
|
|
||||||
|
|
||||||
[Controller]
|
|
||||||
$value: value of the controller (remapped)
|
|
||||||
$id: id of the controller
|
|
||||||
$channel: channel of the controller
|
|
||||||
$rawvalue: original value of the controller
|
|
||||||
|
|
||||||
[Pitch]
|
|
||||||
$value: value of the bend (remapped)
|
|
||||||
$rawvalue: original value of the bend
|
|
||||||
|
|
||||||
[System]
|
|
||||||
$code: hexadecimal code of the system signal
|
|
||||||
|
|
||||||
|
|
||||||
-- [FILE FORMAT] --
|
|
||||||
|
|
||||||
[<device>,<device>]
|
|
||||||
|
|
||||||
- <device> format:
|
|
||||||
{
|
|
||||||
name=<name>
|
|
||||||
commands=[<command>,<command>]
|
|
||||||
}
|
|
||||||
-
|
|
||||||
*name: string referring to client name of the device
|
|
||||||
|
|
||||||
--<command> format (global):
|
|
||||||
{
|
|
||||||
<tag>=<value>
|
|
||||||
<tag>=<value>
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- COMMAND TAGS
|
|
||||||
|
|
||||||
[Global tags]
|
|
||||||
type=<note/controller/pitch/system/connect/disconnect>
|
|
||||||
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/*>
|
|
||||||
trigger=<interval>
|
|
||||||
--
|
|
||||||
*id: note id from 0 to 127
|
|
||||||
> optional, default 0:127
|
|
||||||
*channel: value from 0 to 16 for channel, * for any channel
|
|
||||||
> optional, default *
|
|
||||||
*trigger: note velocity from 0 to 127 that triggers the command
|
|
||||||
> optional, default 1:127
|
|
||||||
|
|
||||||
[Controller tags]
|
|
||||||
id=<interval>
|
|
||||||
channel=<x/*>
|
|
||||||
range=<interval>
|
|
||||||
remap=<interval>
|
|
||||||
float=<true/false>
|
|
||||||
--
|
|
||||||
*id: controller id from 0 to 127
|
|
||||||
> optional, default 0:127
|
|
||||||
*channel: value from 0 to 16 for channel, * for any channel
|
|
||||||
> optional, default *
|
|
||||||
*range: controller value from 0 to 127 that triggers command
|
|
||||||
> optional, default 0:127
|
|
||||||
*remap: remaps the range to given interval
|
|
||||||
> optional, default same as range
|
|
||||||
*float: boolean value defining if output is a floating point value
|
|
||||||
> optional, default false
|
|
||||||
|
|
||||||
[Pitch bend tags]
|
|
||||||
range=<interval>
|
|
||||||
remap=<interval>
|
|
||||||
float=<true/false>
|
|
||||||
--
|
|
||||||
*range: controller value from -8192 to 8191 that triggers command
|
|
||||||
> optional, default -8192:8191
|
|
||||||
*remap: remaps the range to given interval
|
|
||||||
> optional, default same as range
|
|
||||||
*float: boolean value defining if output is a floating point value
|
|
||||||
> optional, default false
|
|
||||||
|
|
||||||
--Interval Format
|
|
||||||
x:y range from x to y
|
|
||||||
x single value x
|
|
||||||
* all possible values
|
|
||||||
|
|
||||||
|
|
||||||
Comments can be done with //
|
|
||||||
note: // in value lines will not be ignored
|
|
||||||
|
|
||||||
A shell command can be written on multiple lines by containing it between '' right after =
|
|
||||||
note: additional ' in the command need to be escaped with \
|
|
||||||
42
README.md
Normal file
42
README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# zmidimap
|
||||||
|
|
||||||
|
Map midi signals coming from ALSA midi devices to shell commands
|
||||||
|
|
||||||
|
Soft dependencies: alsa-utils
|
||||||
|
Hard dependencies: aseqdump
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
### Package
|
||||||
|
|
||||||
|
#### Debian/Ubuntu
|
||||||
|
Download the .deb package then run: `sudo dpkg -i zmidimap.deb ; sudo apt -f install`
|
||||||
|
|
||||||
|
### Standalone
|
||||||
|
|
||||||
|
Move executable `zmidimap` to your PATH directory
|
||||||
|
|
||||||
|
### From source
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/zawwz/zmidimap.git
|
||||||
|
cd zmidimap
|
||||||
|
make && sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
`zmidimap [options] <map file>`
|
||||||
|
This is a daemon program, it does not start any background process by itself and needs to be constantly running for the mapping to be active
|
||||||
|
|
||||||
|
## Map File / Configuration
|
||||||
|
|
||||||
|
See `example.mim` for an example file
|
||||||
|
For more details see `zmidimap --help`
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
To scan for devices use `zmidimap -l`
|
||||||
|
To scan a device's inputs use `zmidimap -p <client name>`
|
||||||
|
|
||||||
|
For more details see `zmidimap --help`
|
||||||
10
gen_help_format.sh
Executable file
10
gen_help_format.sh
Executable file
|
|
@ -0,0 +1,10 @@
|
||||||
|
FORMAT_FOLDER=help_format
|
||||||
|
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 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
|
||||||
|
|
||||||
|
cat $FORMAT_FOLDER/help_template_tail >> $IDIR/help.h
|
||||||
57
help_format/command-tags
Normal file
57
help_format/command-tags
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
-- [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/*>
|
||||||
|
trigger=<interval>
|
||||||
|
--
|
||||||
|
*id: note id from 0 to 127
|
||||||
|
> optional, default 0:127
|
||||||
|
*channel: value from 0 to 16 for channel, * for any channel
|
||||||
|
> optional, default *
|
||||||
|
*trigger: note velocity from 0 to 127 that triggers the command
|
||||||
|
> optional, default 1:127
|
||||||
|
|
||||||
|
[Controller tags]
|
||||||
|
id=<interval>
|
||||||
|
channel=<x/*>
|
||||||
|
range=<interval>
|
||||||
|
remap=<interval>
|
||||||
|
float=<true/false>
|
||||||
|
--
|
||||||
|
*id: controller id from 0 to 127
|
||||||
|
> optional, default 0:127
|
||||||
|
*channel: value from 0 to 16 for channel, * for any channel
|
||||||
|
> optional, default *
|
||||||
|
*range: controller value from 0 to 127 that triggers command
|
||||||
|
> optional, default 0:127
|
||||||
|
*remap: remaps the range to given interval
|
||||||
|
> optional, default same as range
|
||||||
|
*float: boolean value defining if output is a floating point value
|
||||||
|
> optional, default false
|
||||||
|
|
||||||
|
[Pitch bend tags]
|
||||||
|
range=<interval>
|
||||||
|
remap=<interval>
|
||||||
|
float=<true/false>
|
||||||
|
--
|
||||||
|
*range: controller value from -8192 to 8191 that triggers command
|
||||||
|
> optional, default -8192:8191
|
||||||
|
*remap: remaps the range to given interval
|
||||||
|
> optional, default same as range
|
||||||
|
*float: boolean value defining if output is a floating point value
|
||||||
|
> optional, default false
|
||||||
|
|
||||||
|
[Interval Format]
|
||||||
|
x:y range from x to y
|
||||||
|
x single value x
|
||||||
|
* all possible values
|
||||||
23
help_format/file-format
Normal file
23
help_format/file-format
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
-- [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
|
||||||
2
help_format/help_template_head
Normal file
2
help_format/help_template_head
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
#ifndef HELP_H
|
||||||
|
#define HELP_H
|
||||||
1
help_format/help_template_tail
Normal file
1
help_format/help_template_tail
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
#endif //HELP_H
|
||||||
23
help_format/shell-format
Normal file
23
help_format/shell-format
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
-- [SHELL FORMAT] --
|
||||||
|
|
||||||
|
Shell command follows regular shell format
|
||||||
|
|
||||||
|
-- [Environment] --
|
||||||
|
|
||||||
|
[Note]
|
||||||
|
$id: id of the note
|
||||||
|
$channel: channel of the note
|
||||||
|
$velocity: velocity of the note
|
||||||
|
|
||||||
|
[Controller]
|
||||||
|
$value: value of the controller (remapped)
|
||||||
|
$id: id of the controller
|
||||||
|
$channel: channel of the controller
|
||||||
|
$rawvalue: original value of the controller
|
||||||
|
|
||||||
|
[Pitch]
|
||||||
|
$value: value of the bend (remapped)
|
||||||
|
$rawvalue: original value of the bend
|
||||||
|
|
||||||
|
[System]
|
||||||
|
$code: hexadecimal code of the system signal
|
||||||
|
|
@ -9,36 +9,24 @@
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
class file_format_error : public std::exception
|
class format_error : public std::exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
file_format_error(const std::string& what, const std::string& origin, const char* data, int where) { desc=what; index=where; filename=origin; cdat=data; }
|
format_error(const std::string& what, const std::string& origin, const std::string& data, int where) { desc=what; index=where; filename=origin; sdat=data; }
|
||||||
|
|
||||||
const char * what () const throw () {return desc.c_str();}
|
const char * what () const throw () {return desc.c_str();}
|
||||||
const int where () const throw () {return index;}
|
const int where () const throw () {return index;}
|
||||||
const char * data() const throw () {return cdat;}
|
const char * data() const throw () {return sdat.c_str();}
|
||||||
const char * origin() const throw () {return filename.c_str();}
|
const char * origin() const throw () {return filename.c_str();}
|
||||||
private:
|
private:
|
||||||
std::string desc;
|
std::string desc;
|
||||||
int index;
|
int index;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
const char* cdat;
|
std::string sdat;
|
||||||
};
|
};
|
||||||
|
|
||||||
class chunk_format_error : public std::exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
chunk_format_error(std::string const& what, int where) { desc=what; index=where; }
|
|
||||||
|
|
||||||
const char * what () const throw () {return desc.c_str();}
|
|
||||||
const int where () const throw () {return index;}
|
|
||||||
private:
|
|
||||||
std::string desc;
|
|
||||||
int index;
|
|
||||||
};
|
|
||||||
|
|
||||||
void printFileException(file_format_error& exc);
|
|
||||||
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin);
|
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin);
|
||||||
|
inline void printFormatException(format_error& exc) { printErrorIndex(exc.data(), exc.where(), exc.what(), exc.origin()); }
|
||||||
|
|
||||||
class Filedat;
|
class Filedat;
|
||||||
|
|
||||||
|
|
@ -59,12 +47,18 @@ protected:
|
||||||
class Chunk
|
class Chunk
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Chunk(const char* in, const int in_size, int offset=0, Filedat* data=nullptr)
|
Chunk() { m_achunk=nullptr; }
|
||||||
{ m_achunk=nullptr; set(in, in_size, offset, data);}
|
|
||||||
Chunk(std::string const& in, int offset=0, Filedat* data=nullptr)
|
|
||||||
{ m_achunk=nullptr; set(in, offset, data);}
|
|
||||||
|
|
||||||
Chunk(Chunk const& in) { m_achunk=nullptr; set(in);}
|
Chunk(const char* in)
|
||||||
|
{ m_achunk=nullptr; set(in, strlen(in), 0, nullptr); }
|
||||||
|
Chunk(std::string const& in)
|
||||||
|
{ m_achunk=nullptr; set(in, 0, nullptr); }
|
||||||
|
Chunk(const char* in, const int in_size, int offset=0, Filedat* data=nullptr)
|
||||||
|
{ m_achunk=nullptr; set(in, in_size, offset, data); }
|
||||||
|
Chunk(std::string const& in, int offset=0, Filedat* data=nullptr)
|
||||||
|
{ m_achunk=nullptr; set(in, offset, data); }
|
||||||
|
|
||||||
|
Chunk(Chunk const& in) { m_achunk=nullptr; set(in); }
|
||||||
|
|
||||||
void clear() { if(m_achunk!=nullptr) delete m_achunk; m_achunk=nullptr; }
|
void clear() { if(m_achunk!=nullptr) delete m_achunk; m_achunk=nullptr; }
|
||||||
~Chunk() { clear(); }
|
~Chunk() { clear(); }
|
||||||
|
|
@ -72,7 +66,6 @@ public:
|
||||||
Filedat* parent() const { return m_parent; }
|
Filedat* parent() const { return m_parent; }
|
||||||
int offset() const { return m_offset; }
|
int offset() const { return m_offset; }
|
||||||
|
|
||||||
|
|
||||||
void set(const char* in, const int in_size, int offset=0, Filedat* data=nullptr);
|
void set(const char* in, const int in_size, int offset=0, Filedat* data=nullptr);
|
||||||
void set(std::string const& in, int offset=0, Filedat* data=nullptr) { this->set(in.c_str(), in.size(), offset, data); }
|
void set(std::string const& in, int offset=0, Filedat* data=nullptr) { this->set(in.c_str(), in.size(), offset, data); }
|
||||||
|
|
||||||
|
|
@ -81,16 +74,16 @@ public:
|
||||||
std::string strval(unsigned int alignment=0, std::string const& aligner="\t") const;
|
std::string strval(unsigned int alignment=0, std::string const& aligner="\t") const;
|
||||||
|
|
||||||
|
|
||||||
// bool concatenate(Chunk const& chk); //concatenates chunks
|
void addToChunk(std::string const& name, Chunk const& val); //adds if datachunk
|
||||||
bool addToChunk(std::string const& name, Chunk const& val); //adds if datachunk
|
void addToChunk(std::vector<std::pair<std::string, Chunk>> const& vec); //adds if datachunk
|
||||||
bool addToChunk(std::vector<std::pair<std::string, Chunk>> const& vec); //adds if datachunk
|
void addToList(Chunk const& val); //adds if list
|
||||||
bool addToList(Chunk const& val); //adds if list
|
void addToList(std::vector<Chunk> const& vec); //adds if list
|
||||||
bool addToList(std::vector<Chunk> const& vec); //adds if list
|
inline void add(std::string const& name, Chunk const& val) { addToChunk(name, val); } //adds if datachunk
|
||||||
inline bool add(std::string const& name, Chunk const& val) { return addToChunk(name, val); } //adds if datachunk
|
inline void add(std::pair<std::string, Chunk> const& pair) { add(pair.first, pair.second); } //adds if datachunk
|
||||||
inline bool add(std::pair<std::string, Chunk> const& pair) { return add(pair.first, pair.second); } //adds if datachunk
|
inline void add(std::vector<std::pair<std::string, Chunk>> const& vec) { addToChunk(vec); } //adds if datachunk
|
||||||
inline bool add(std::vector<std::pair<std::string, Chunk>> const& vec) { return addToChunk(vec); } //adds if datachunk
|
inline void add(Chunk const& val) { addToList(val); } //adds if list
|
||||||
inline bool add(Chunk const& val) { return addToList(val); } //adds if list
|
inline void add(std::vector<Chunk> const& vec) { addToList(vec); } //adds if list
|
||||||
inline bool add(std::vector<Chunk> const& vec) { return addToList(vec); } //adds if list
|
// void concatenate(Chunk const& chk); //concatenates chunks
|
||||||
|
|
||||||
Chunk copy() const { return Chunk(*this); }
|
Chunk copy() const { return Chunk(*this); }
|
||||||
Chunk* pcopy() const { return new Chunk(*this); }
|
Chunk* pcopy() const { return new Chunk(*this); }
|
||||||
|
|
@ -104,13 +97,13 @@ public:
|
||||||
Chunk& subChunkRef(std::string const& a) const; //datachunk
|
Chunk& subChunkRef(std::string const& a) const; //datachunk
|
||||||
Chunk& subChunkRef(unsigned int a) const; //chunklist
|
Chunk& subChunkRef(unsigned int a) const; //chunklist
|
||||||
|
|
||||||
Chunk& operator[](std::string const& a) const { return subChunkRef(a); }
|
Chunk& operator[](std::string const& a) const { return subChunkRef(a); }
|
||||||
Chunk& operator[](unsigned int a) const { return subChunkRef(a); }
|
Chunk& operator[](unsigned int a) const { return subChunkRef(a); }
|
||||||
Chunk& operator=(Chunk const& a) { set(a); return *this; }
|
Chunk& operator=(Chunk const& a) { set(a); return *this; }
|
||||||
inline bool operator+=(std::pair<std::string, Chunk> const& a) { return add(a); }
|
inline Chunk& operator+=(std::pair<std::string, Chunk> const& a) { add(a); return *this; }
|
||||||
inline bool operator+=(std::vector<std::pair<std::string, Chunk>> const& a) { return add(a); }
|
inline Chunk& operator+=(std::vector<std::pair<std::string, Chunk>> const& a) { add(a); return *this; }
|
||||||
inline bool operator+=(Chunk const& a) { return add(a); }
|
inline Chunk& operator+=(Chunk const& a) { add(a); return *this; }
|
||||||
inline bool operator+=(std::vector<Chunk> const& a) { return add(a); }
|
inline Chunk& operator+=(std::vector<Chunk> const& a) { add(a); return *this; }
|
||||||
// inline bool operator*=(Chunk const& a) { concatenate(a); }
|
// inline bool operator*=(Chunk const& a) { concatenate(a); }
|
||||||
|
|
||||||
//add operator+ and operator*
|
//add operator+ and operator*
|
||||||
|
|
@ -162,8 +155,10 @@ public:
|
||||||
|
|
||||||
bool readTest() const;
|
bool readTest() const;
|
||||||
|
|
||||||
void importFile();
|
void import_file(const std::string& path="");
|
||||||
bool exportFile(std::string const& path="", std::string const& aligner="\t") const;
|
void import_stdin();
|
||||||
|
void import_string(const std::string& data);
|
||||||
|
bool export_file(std::string const& path="", std::string const& aligner="\t") const;
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
|
@ -172,8 +167,8 @@ public:
|
||||||
|
|
||||||
std::string strval(std::string const& aligner="\t") const;
|
std::string strval(std::string const& aligner="\t") const;
|
||||||
|
|
||||||
Chunk* pchunk() const { return m_dataChunk; }
|
inline Chunk* pchunk() const { return m_dataChunk; }
|
||||||
Chunk& chunk() const { return *m_dataChunk; }
|
inline Chunk& chunk() const { return *m_dataChunk; }
|
||||||
|
|
||||||
inline Chunk* pdata() const { return m_dataChunk; }
|
inline Chunk* pdata() const { return m_dataChunk; }
|
||||||
inline Chunk& data() const { return *m_dataChunk; }
|
inline Chunk& data() const { return *m_dataChunk; }
|
||||||
|
|
@ -181,16 +176,13 @@ public:
|
||||||
inline const std::string& stringdata() const { return m_data; }
|
inline const std::string& stringdata() const { return m_data; }
|
||||||
inline const char* c_data() const { return m_data.c_str(); }
|
inline const char* c_data() const { return m_data.c_str(); }
|
||||||
|
|
||||||
Chunk& operator[](const std::string& index)
|
inline Chunk& operator[](const std::string& index) { return m_dataChunk->subChunkRef(index); }
|
||||||
{
|
inline Chunk& operator[](const int index) { return m_dataChunk->subChunkRef(index); }
|
||||||
return m_dataChunk->subChunkRef(index);
|
|
||||||
}
|
|
||||||
Chunk& operator[](const int index)
|
|
||||||
{
|
|
||||||
return m_dataChunk->subChunkRef(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
//functions
|
||||||
|
void generateChunk();
|
||||||
|
//attributes
|
||||||
std::string m_filePath;
|
std::string m_filePath;
|
||||||
std::string m_data;
|
std::string m_data;
|
||||||
Chunk* m_dataChunk;
|
Chunk* m_dataChunk;
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,13 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
|
|
||||||
#include "Filedat.hpp"
|
#include "Filedat.hpp"
|
||||||
|
|
||||||
|
#define KILL_COMMAND_FH "kill -s INT $(pgrep -f \"aseqdump -p "
|
||||||
|
#define KILL_COMMAND_SH "\")"
|
||||||
|
|
||||||
|
void sh(std::string const& string);
|
||||||
|
|
||||||
class Device
|
class Device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -22,6 +26,7 @@ public:
|
||||||
Chunk export_chunk();
|
Chunk export_chunk();
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
|
int client_id;
|
||||||
bool busy;
|
bool busy;
|
||||||
|
|
||||||
uint32_t nb_command;
|
uint32_t nb_command;
|
||||||
|
|
|
||||||
6
include/help.h
Normal file
6
include/help.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#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 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"
|
||||||
|
#endif //HELP_H
|
||||||
|
|
@ -9,43 +9,82 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
|
// Turn argc/argv into a vector<string>
|
||||||
std::vector<std::string> argVector(int argc, char** argv);
|
std::vector<std::string> argVector(int argc, char** argv);
|
||||||
|
|
||||||
class Option
|
class Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/* CTOR/DTOR */
|
||||||
|
//ctors
|
||||||
Option();
|
Option();
|
||||||
Option(char c, bool arg);
|
Option(char c, bool arg, std::string helptext="", std::string argname="arg");
|
||||||
Option(std::string const& str, bool arg);
|
Option(std::string const& str, bool arg, std::string helptext="", std::string argname="arg");
|
||||||
Option(char c, std::string const& str, bool arg);
|
Option(char c, std::string const& str, bool arg, std::string helptext="", std::string argname="arg");
|
||||||
|
//dtors
|
||||||
virtual ~Option();
|
virtual ~Option();
|
||||||
|
|
||||||
bool shortDef;
|
/* FUNCTIONS */
|
||||||
|
|
||||||
|
// Print command help. Puts leftpad spaces before printing, and rightpad space until help
|
||||||
|
void printHelp(int leftpad, int rightpad);
|
||||||
|
|
||||||
|
/* PROPERTIES */
|
||||||
|
|
||||||
|
bool shortDef; // has a char definition
|
||||||
char charName;
|
char charName;
|
||||||
|
|
||||||
bool longDef;
|
bool longDef; // has a string definition
|
||||||
std::string strName;
|
std::string strName;
|
||||||
|
|
||||||
bool activated;
|
bool takesArgument; // option takes an argument
|
||||||
|
|
||||||
|
std::string help_text; // text to display in printHelp
|
||||||
|
std::string arg_name; // name of the argument to display in printHelp
|
||||||
|
|
||||||
|
/* PROCESSING STATUS */
|
||||||
|
|
||||||
|
bool activated; // option was activated
|
||||||
|
|
||||||
|
std::string argument; // argument of the option
|
||||||
|
|
||||||
bool takesArgument;
|
|
||||||
std::string argument;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class OptionSet
|
class OptionSet
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/* CTOR/DTOR */
|
||||||
OptionSet();
|
OptionSet();
|
||||||
virtual ~OptionSet();
|
virtual ~OptionSet();
|
||||||
|
|
||||||
void addOption(Option opt) { m_options.push_back(opt); }
|
/* PROPERTIES */
|
||||||
|
|
||||||
Option* findOption(char c);
|
|
||||||
Option* findOption(std::string const& str);
|
|
||||||
|
|
||||||
|
// Stream on which errors are sent. Default stderr
|
||||||
std::ostream* errStream;
|
std::ostream* errStream;
|
||||||
|
|
||||||
|
/* FUNCTIONS */
|
||||||
|
|
||||||
|
/*CREATION FUNCTIONS*/
|
||||||
|
//Adding an option. Refer to Option ctors
|
||||||
|
void addOption(Option opt) { m_options.push_back(opt); }
|
||||||
|
|
||||||
|
/*PRINT FUNCTIONS*/
|
||||||
|
// Print command help. Puts leftpad spaces before each line, and rightpad space until help
|
||||||
|
void printHelp(int leftpad=2, int rightpad=25);
|
||||||
|
|
||||||
|
/*QUERY FUNCTIONS*/
|
||||||
|
// Find an option with its charname
|
||||||
|
Option* findOption(char c);
|
||||||
|
// Find an option with its stringname
|
||||||
|
Option* findOption(std::string const& str);
|
||||||
|
|
||||||
|
/*PROCESSING FUNCTIONS*/
|
||||||
|
// Process through options.
|
||||||
|
// pair.first : vector with arguments that were not identified as options
|
||||||
|
// pair.second : bool indicating status. True if no error encountered, false if errors
|
||||||
std::pair<std::vector<std::string>,bool> getOptions(std::vector<std::string> input);
|
std::pair<std::vector<std::string>,bool> getOptions(std::vector<std::string> input);
|
||||||
|
std::pair<std::vector<std::string>,bool> getOptions(int argc, char** argv) { return getOptions(argVector(argc, argv)); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Option> m_options;
|
std::vector<Option> m_options;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
#ifndef SYSTEM_HPP
|
#ifndef SYSTEM_HPP
|
||||||
#define SYSTEM_HPP
|
#define SYSTEM_HPP
|
||||||
|
|
||||||
|
#define ANNOUNCE_COMMAND "aseqdump -p System:1"
|
||||||
|
#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"
|
||||||
|
|
||||||
void device_check();
|
void device_check();
|
||||||
|
|
||||||
void announce_loop();
|
void announce_loop();
|
||||||
|
|
|
||||||
232
src/Filedat.cpp
232
src/Filedat.cpp
|
|
@ -18,57 +18,72 @@ std::string _repeatString(const std::string& str, const unsigned int n)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void printFileException(file_format_error& exc)
|
|
||||||
{
|
|
||||||
printErrorIndex(exc.data(), exc.where(), exc.what(), exc.origin());
|
|
||||||
}
|
|
||||||
|
|
||||||
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin)
|
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin)
|
||||||
{
|
{
|
||||||
int i=0, j=0; // j: last newline
|
int i=0, j=0; // j: last newline
|
||||||
int line=1; //n: line #
|
int line=1; //n: line #
|
||||||
int in_size=strlen(in);
|
int in_size=strlen(in);
|
||||||
while(i < in_size && i < index)
|
if(index >= 0)
|
||||||
{
|
{
|
||||||
if(in[i] == '\n')
|
while(i < in_size && i < index)
|
||||||
{
|
{
|
||||||
line++;
|
if(in[i] == '\n')
|
||||||
j=i+1;
|
{
|
||||||
|
line++;
|
||||||
|
j=i+1;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
while(i < in_size && in[i]!='\n')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
while(i < in_size && in[i]!='\n')
|
if(origin != "")
|
||||||
{
|
{
|
||||||
i++;
|
std::cerr << origin << ": Error\nLine " << line << " col " << index-j+1 << ": " << message << std::endl;
|
||||||
|
std::cerr << std::string(in+j, i-j) << std::endl;
|
||||||
|
std::cerr << _repeatString(" ", index-j) << '^' << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Format Error: " << message << std::endl;
|
||||||
|
if(index >= 0)
|
||||||
|
{
|
||||||
|
std::cerr << std::string(in, i) << std::endl;
|
||||||
|
std::cerr << _repeatString(" ", index-j) << '^' << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
std::cerr << in << std::endl;
|
||||||
}
|
}
|
||||||
std::cerr << origin << ": Error\nLine " << line << " col " << index-j << ": " << message << std::endl;
|
|
||||||
std::cerr << std::string(in+j, i-j) << std::endl;
|
|
||||||
std::cerr << _repeatString(" ", index-j) << '^' << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Filedat::Filedat()
|
Filedat::Filedat()
|
||||||
{
|
{
|
||||||
m_dataChunk = nullptr;
|
m_dataChunk = new Chunk();
|
||||||
}
|
}
|
||||||
|
|
||||||
Filedat::Filedat(std::string const& in)
|
Filedat::Filedat(std::string const& in)
|
||||||
{
|
{
|
||||||
m_dataChunk = nullptr;
|
m_dataChunk = new Chunk();
|
||||||
m_filePath=in;
|
m_filePath=in;
|
||||||
}
|
}
|
||||||
|
|
||||||
Filedat::~Filedat()
|
Filedat::~Filedat()
|
||||||
{
|
{
|
||||||
this->clear();
|
if(m_dataChunk!=nullptr)
|
||||||
|
delete m_dataChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filedat::clear()
|
void Filedat::clear()
|
||||||
{
|
{
|
||||||
|
m_data="";
|
||||||
if(m_dataChunk!=nullptr)
|
if(m_dataChunk!=nullptr)
|
||||||
{
|
{
|
||||||
delete m_dataChunk;
|
delete m_dataChunk;
|
||||||
m_dataChunk = nullptr;
|
m_dataChunk = new Chunk();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,35 +96,47 @@ bool Filedat::readTest() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filedat::importFile()
|
void Filedat::import_file(const std::string& path)
|
||||||
{
|
{
|
||||||
std::ifstream stream(m_filePath);
|
if(path != "")
|
||||||
if(!stream)
|
m_filePath=path;
|
||||||
{
|
std::ifstream st(m_filePath);
|
||||||
throw std::runtime_error("Cannot open file '" + m_filePath + '\'');
|
if(!st)
|
||||||
}
|
throw std::runtime_error("Cannot read file '" + m_filePath + '\'');
|
||||||
|
|
||||||
m_data="";
|
|
||||||
std::string line;
|
|
||||||
|
|
||||||
while(stream)
|
|
||||||
{
|
|
||||||
getline(stream, line);
|
|
||||||
m_data += (line + '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
this->clear();
|
this->clear();
|
||||||
try
|
std::string line;
|
||||||
|
|
||||||
|
while(st)
|
||||||
{
|
{
|
||||||
m_dataChunk = new Chunk(m_data.c_str(), m_data.size(), 0, this);
|
getline(st, line);
|
||||||
}
|
m_data += (line + '\n');
|
||||||
catch(chunk_format_error& e)
|
|
||||||
{
|
|
||||||
throw file_format_error(e.what(), m_filePath, m_data.c_str(), e.where());
|
|
||||||
}
|
}
|
||||||
|
this->generateChunk();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Filedat::exportFile(std::string const& path, std::string const& aligner) const
|
void Filedat::import_stdin()
|
||||||
|
{
|
||||||
|
m_filePath="stdin";
|
||||||
|
this->clear();
|
||||||
|
std::string line;
|
||||||
|
while(std::cin)
|
||||||
|
{
|
||||||
|
getline(std::cin, line);
|
||||||
|
m_data += (line + '\n');
|
||||||
|
}
|
||||||
|
this->generateChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Filedat::import_string(const std::string& data)
|
||||||
|
{
|
||||||
|
this->clear();
|
||||||
|
m_data=data;
|
||||||
|
m_filePath="";
|
||||||
|
this->generateChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Filedat::export_file(std::string const& path, std::string const& aligner) const
|
||||||
{
|
{
|
||||||
std::ofstream stream;
|
std::ofstream stream;
|
||||||
if(path=="")
|
if(path=="")
|
||||||
|
|
@ -130,6 +157,20 @@ std::string Filedat::strval(std::string const& aligner) const
|
||||||
return m_dataChunk->strval(0, aligner);
|
return m_dataChunk->strval(0, aligner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Filedat::generateChunk()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(m_dataChunk != nullptr)
|
||||||
|
delete m_dataChunk;
|
||||||
|
m_dataChunk = new Chunk(m_data.c_str(), m_data.size(), 0, this);
|
||||||
|
}
|
||||||
|
catch(format_error& e)
|
||||||
|
{
|
||||||
|
throw format_error(e.what(), m_filePath, m_data, e.where());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string _getname(const char* in, const int in_size, int* start, int* val_size, int* end)
|
std::string _getname(const char* in, const int in_size, int* start, int* val_size, int* end)
|
||||||
{
|
{
|
||||||
int i=0;
|
int i=0;
|
||||||
|
|
@ -156,12 +197,10 @@ std::string _getname(const char* in, const int in_size, int* start, int* val_siz
|
||||||
while(i<in_size && in[i] != '=') //skip to =
|
while(i<in_size && in[i] != '=') //skip to =
|
||||||
i++;
|
i++;
|
||||||
if(i >= in_size) //no =
|
if(i >= in_size) //no =
|
||||||
{
|
throw format_error("Tag has no value", "", std::string(in, in_size), j);
|
||||||
throw chunk_format_error("Tag has no value", j);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i == j) //nothing preceding =
|
if(i == j) //nothing preceding =
|
||||||
throw chunk_format_error("Value has no tag", i);
|
throw format_error("Value has no tag", "", std::string(in, in_size), i);
|
||||||
|
|
||||||
int k=i-1; //name end
|
int k=i-1; //name end
|
||||||
while( !_isRead(in[k]) )
|
while( !_isRead(in[k]) )
|
||||||
|
|
@ -190,7 +229,7 @@ std::string _getname(const char* in, const int in_size, int* start, int* val_siz
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if(i+j >= in_size) // no closing "
|
if(i+j >= in_size) // no closing "
|
||||||
throw chunk_format_error("Double quote does not close", i-1);
|
throw format_error("Double quote does not close", "", std::string(in, in_size), i-1);
|
||||||
*val_size=j;
|
*val_size=j;
|
||||||
*end=i+j+1;
|
*end=i+j+1;
|
||||||
return name;
|
return name;
|
||||||
|
|
@ -207,7 +246,7 @@ std::string _getname(const char* in, const int in_size, int* start, int* val_siz
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if(i+j >= in_size) // no closing '
|
if(i+j >= in_size) // no closing '
|
||||||
throw chunk_format_error("Single quote does not close", i-1);
|
throw format_error("Single quote does not close", "", std::string(in, in_size), i-1);
|
||||||
*val_size=j;
|
*val_size=j;
|
||||||
*end=i+j+1;
|
*end=i+j+1;
|
||||||
return name;
|
return name;
|
||||||
|
|
@ -231,7 +270,7 @@ std::string _getname(const char* in, const int in_size, int* start, int* val_siz
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if(i+j >= in_size) //reached end without closing
|
if(i+j >= in_size) //reached end without closing
|
||||||
throw chunk_format_error("Brace does not close", i);
|
throw format_error("Brace does not close", "", std::string(in, in_size), i);
|
||||||
j++;
|
j++;
|
||||||
*val_size=j;
|
*val_size=j;
|
||||||
*end=i+j;
|
*end=i+j;
|
||||||
|
|
@ -256,7 +295,7 @@ std::string _getname(const char* in, const int in_size, int* start, int* val_siz
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if(i+j >= in_size) //reached end without closing
|
if(i+j >= in_size) //reached end without closing
|
||||||
throw chunk_format_error("Bracket does not close", i);
|
throw format_error("Bracket does not close", "", std::string(in, in_size), i);
|
||||||
j++;
|
j++;
|
||||||
*val_size=j;
|
*val_size=j;
|
||||||
*end=i+j;
|
*end=i+j;
|
||||||
|
|
@ -322,7 +361,7 @@ std::string _getlist(const char* in, const int in_size, int* start, int* end)
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if(i+j >= in_size) // no closing "
|
if(i+j >= in_size) // no closing "
|
||||||
throw chunk_format_error("Double quote does not close", i-1);
|
throw format_error("Double quote does not close", "", std::string(in, in_size), i-1);
|
||||||
ret = std::string(in+i, j);
|
ret = std::string(in+i, j);
|
||||||
*end=i+j+1;
|
*end=i+j+1;
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +376,7 @@ std::string _getlist(const char* in, const int in_size, int* start, int* end)
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if(i+j >= in_size) // no closing '
|
if(i+j >= in_size) // no closing '
|
||||||
throw chunk_format_error("Single quote does not close", i-1);
|
throw format_error("Single quote does not close", "", std::string(in, in_size), i-1);
|
||||||
ret = std::string(in+i, j);
|
ret = std::string(in+i, j);
|
||||||
*end=i+j+1;
|
*end=i+j+1;
|
||||||
}
|
}
|
||||||
|
|
@ -359,7 +398,7 @@ std::string _getlist(const char* in, const int in_size, int* start, int* end)
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if(i+j >= in_size) //reached end without closing
|
if(i+j >= in_size) //reached end without closing
|
||||||
throw chunk_format_error("Brace does not close", i);
|
throw format_error("Brace does not close", "", std::string(in, in_size), i);
|
||||||
j++;
|
j++;
|
||||||
ret = std::string(in+i, j);
|
ret = std::string(in+i, j);
|
||||||
*end=i+j;
|
*end=i+j;
|
||||||
|
|
@ -382,7 +421,7 @@ std::string _getlist(const char* in, const int in_size, int* start, int* end)
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
if(i+j >= in_size) //reached end without closing
|
if(i+j >= in_size) //reached end without closing
|
||||||
throw chunk_format_error("Bracket does not close", i);
|
throw format_error("Bracket does not close", "", std::string(in, in_size), i);
|
||||||
j++;
|
j++;
|
||||||
ret = std::string(in+i, j);
|
ret = std::string(in+i, j);
|
||||||
*end=i+j;
|
*end=i+j;
|
||||||
|
|
@ -419,7 +458,7 @@ std::string _getlist(const char* in, const int in_size, int* start, int* end)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
else //Unexpected char
|
else //Unexpected char
|
||||||
throw chunk_format_error("Expecting comma", i);
|
throw format_error("Expecting comma", "", std::string(in, in_size), i);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -448,7 +487,7 @@ void Chunk::set(const char* in, const int in_size, int offset, Filedat* data)
|
||||||
while(!_isRead(in[val_end])) //skip unread char
|
while(!_isRead(in[val_end])) //skip unread char
|
||||||
val_end--;
|
val_end--;
|
||||||
if(in[val_end] != '}')
|
if(in[val_end] != '}')
|
||||||
throw chunk_format_error("Expecting closing brace", val_end-1);
|
throw format_error("Expecting closing brace", "", std::string(in, in_size), val_end+1);
|
||||||
|
|
||||||
DataChunk* tch = new DataChunk();
|
DataChunk* tch = new DataChunk();
|
||||||
m_achunk = tch;
|
m_achunk = tch;
|
||||||
|
|
@ -461,15 +500,18 @@ void Chunk::set(const char* in, const int in_size, int offset, Filedat* data)
|
||||||
int _size=0;
|
int _size=0;
|
||||||
int end=0;
|
int end=0;
|
||||||
|
|
||||||
|
while(!_isRead(in[i]))
|
||||||
|
i++;
|
||||||
|
|
||||||
std::string newstr=std::string(in+i, val_end-i);
|
std::string newstr=std::string(in+i, val_end-i);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
name = _getname(newstr.c_str(), newstr.size(), &start, &_size, &end);
|
name = _getname(newstr.c_str(), newstr.size(), &start, &_size, &end);
|
||||||
val = newstr.substr(start, _size);
|
val = newstr.substr(start, _size);
|
||||||
}
|
}
|
||||||
catch(chunk_format_error& e)
|
catch(format_error& e)
|
||||||
{
|
{
|
||||||
throw chunk_format_error(e.what(), e.where()+i);
|
throw format_error(e.what(), "", std::string(in, in_size), e.where()+i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( name == "" ) //no more values
|
if( name == "" ) //no more values
|
||||||
|
|
@ -477,11 +519,16 @@ void Chunk::set(const char* in, const int in_size, int offset, Filedat* data)
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tch->values.insert(std::make_pair(name, new Chunk(val.c_str(),val.size(), offset + start+i, m_parent) ));
|
Chunk* chk = new Chunk(val.c_str(),val.size(), offset + start+i, m_parent);
|
||||||
|
if(!tch->values.insert( std::make_pair(name, chk ) ).second)
|
||||||
|
{
|
||||||
|
delete chk;
|
||||||
|
throw format_error("Key '" + name + "' already present", "", std::string(in, in_size), 0 - start );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(chunk_format_error& e)
|
catch(format_error& e)
|
||||||
{
|
{
|
||||||
throw chunk_format_error(e.what(), e.where() + start + i );
|
throw format_error(e.what(), "", std::string(in, in_size), e.where() + start + i );
|
||||||
}
|
}
|
||||||
|
|
||||||
i += end;
|
i += end;
|
||||||
|
|
@ -497,7 +544,7 @@ void Chunk::set(const char* in, const int in_size, int offset, Filedat* data)
|
||||||
while(!_isRead(in[val_end])) //skip unread char
|
while(!_isRead(in[val_end])) //skip unread char
|
||||||
val_end--;
|
val_end--;
|
||||||
if(in[val_end] != ']')
|
if(in[val_end] != ']')
|
||||||
throw chunk_format_error("Expecting closing bracket", val_end-1);
|
throw format_error("Expecting closing bracket", "", std::string(in, in_size), val_end+1);
|
||||||
|
|
||||||
ChunkList* tch = new ChunkList();
|
ChunkList* tch = new ChunkList();
|
||||||
m_achunk = tch;
|
m_achunk = tch;
|
||||||
|
|
@ -511,18 +558,18 @@ void Chunk::set(const char* in, const int in_size, int offset, Filedat* data)
|
||||||
{
|
{
|
||||||
val = _getlist(newstr.c_str(), newstr.size(), &start, &end);
|
val = _getlist(newstr.c_str(), newstr.size(), &start, &end);
|
||||||
}
|
}
|
||||||
catch(chunk_format_error& e)
|
catch(format_error& e)
|
||||||
{
|
{
|
||||||
throw chunk_format_error(e.what(), e.where()+i);
|
throw format_error(e.what(), "", std::string(in, in_size), e.where()+i);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tch->list.push_back(new Chunk(val.c_str(),val.size(), offset + start+i, m_parent) );
|
tch->list.push_back(new Chunk(val.c_str(),val.size(), offset + start+i, m_parent) );
|
||||||
}
|
}
|
||||||
catch(chunk_format_error& e)
|
catch(format_error& e)
|
||||||
{
|
{
|
||||||
throw chunk_format_error(e.what(), e.where() + start + i );
|
throw format_error(e.what(), "", std::string(in, in_size), e.where() + start + i );
|
||||||
}
|
}
|
||||||
|
|
||||||
i+=end;
|
i+=end;
|
||||||
|
|
@ -548,60 +595,59 @@ void Chunk::set(const char* in, const int in_size, int offset, Filedat* data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunk::addToChunk(std::string const& name, Chunk const& val)
|
void Chunk::addToChunk(std::string const& name, Chunk const& val)
|
||||||
{
|
{
|
||||||
if(this->type()==AbstractChunk::chunk)
|
if(this->type()==AbstractChunk::chunk)
|
||||||
{
|
{
|
||||||
DataChunk* cp = dynamic_cast<DataChunk*>(m_achunk);
|
DataChunk* cp = dynamic_cast<DataChunk*>(m_achunk);
|
||||||
cp->values.insert(std::make_pair(name , new Chunk(val)));
|
Chunk* chk = new Chunk(val);
|
||||||
return true;
|
if( !cp->values.insert( std::make_pair(name,chk) ).second )
|
||||||
|
{
|
||||||
|
delete chk;
|
||||||
|
throw format_error("Key '" + name + "' already present", "", this->strval(), -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(this->type() == AbstractChunk::none)
|
else if(this->type() == AbstractChunk::none)
|
||||||
{
|
{
|
||||||
DataChunk* cp = new DataChunk();
|
DataChunk* cp = new DataChunk();
|
||||||
cp->values.insert(std::make_pair(name , new Chunk(val)));
|
|
||||||
m_achunk=cp;
|
m_achunk=cp;
|
||||||
return true;
|
cp->values.insert(std::make_pair(name , new Chunk(val)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
{
|
||||||
|
throw format_error("Cannot add keys to non-map chunks", "", this->strval(), -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunk::addToChunk(std::vector<std::pair<std::string, Chunk>> const& vec)
|
void Chunk::addToChunk(std::vector<std::pair<std::string, Chunk>> const& vec)
|
||||||
{
|
{
|
||||||
if(this->type()!=AbstractChunk::chunk && this->type()!=AbstractChunk::none)
|
|
||||||
return false;
|
|
||||||
for(auto it : vec)
|
for(auto it : vec)
|
||||||
this->add(it.first, it.second);
|
this->addToChunk(it.first, it.second);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunk::addToList(Chunk const& val)
|
void Chunk::addToList(Chunk const& val)
|
||||||
{
|
{
|
||||||
if(this->type()==AbstractChunk::list)
|
if(this->type()==AbstractChunk::list)
|
||||||
{
|
{
|
||||||
ChunkList* lp = dynamic_cast<ChunkList*>(m_achunk);
|
ChunkList* lp = dynamic_cast<ChunkList*>(m_achunk);
|
||||||
lp->list.push_back(new Chunk(val));
|
lp->list.push_back(new Chunk(val));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else if(this->type() == AbstractChunk::none)
|
else if(this->type() == AbstractChunk::none)
|
||||||
{
|
{
|
||||||
ChunkList* lp = new ChunkList();
|
ChunkList* lp = new ChunkList();
|
||||||
lp->list.push_back(new Chunk(val));
|
|
||||||
m_achunk=lp;
|
m_achunk=lp;
|
||||||
return true;
|
lp->list.push_back(new Chunk(val));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
{
|
||||||
|
throw format_error("Cannot add elements to non-list chunks", "", this->strval(), -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunk::addToList(std::vector<Chunk> const& vec)
|
void Chunk::addToList(std::vector<Chunk> const& vec)
|
||||||
{
|
{
|
||||||
if(this->type()!=AbstractChunk::chunk && this->type()!=AbstractChunk::none)
|
|
||||||
return false;
|
|
||||||
for(auto it : vec)
|
for(auto it : vec)
|
||||||
this->add(it);
|
this->addToList(it);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Chunk::strval(unsigned int alignment, std::string const& aligner) const
|
std::string Chunk::strval(unsigned int alignment, std::string const& aligner) const
|
||||||
|
|
@ -693,11 +739,11 @@ Chunk& Chunk::subChunkRef(std::string const& in) const
|
||||||
{
|
{
|
||||||
if(m_parent != nullptr)
|
if(m_parent != nullptr)
|
||||||
{
|
{
|
||||||
throw file_format_error("Element isn't a {}", m_parent->filePath(), m_parent->c_data(), m_offset );
|
throw format_error("Chunk isn't a map", m_parent->filePath(), m_parent->stringdata(), m_offset );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw chunk_format_error("Chunk isn't a {}", m_offset);
|
throw format_error("Chunk isn't a map", "", this->strval(), -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataChunk* dc = dynamic_cast<DataChunk*>(m_achunk);
|
DataChunk* dc = dynamic_cast<DataChunk*>(m_achunk);
|
||||||
|
|
@ -706,11 +752,11 @@ Chunk& Chunk::subChunkRef(std::string const& in) const
|
||||||
{
|
{
|
||||||
if(m_parent != nullptr)
|
if(m_parent != nullptr)
|
||||||
{
|
{
|
||||||
throw file_format_error("Chunk doesn't have '" + in + "' flag", m_parent->filePath(), m_parent->c_data(), m_offset );
|
throw format_error("Map doesn't have '" + in + "' flag", m_parent->filePath(), m_parent->stringdata(), m_offset );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw chunk_format_error("Chunk doesn't have '" + in + "' flag", m_offset );
|
throw format_error("Map doesn't have '" + in + "' flag", "", this->strval(), -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return *fi->second;
|
return *fi->second;
|
||||||
|
|
@ -722,11 +768,11 @@ Chunk& Chunk::subChunkRef(unsigned int a) const
|
||||||
{
|
{
|
||||||
if(m_parent != nullptr)
|
if(m_parent != nullptr)
|
||||||
{
|
{
|
||||||
throw file_format_error("Element isn't a {}", m_parent->filePath(), m_parent->c_data(), m_offset );
|
throw format_error("Chunk isn't a list", m_parent->filePath(), m_parent->stringdata(), m_offset );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw chunk_format_error("Chunk isn't a {}", m_offset);
|
throw format_error("Chunk isn't a list", "", this->strval(), -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ChunkList* cl = dynamic_cast<ChunkList*>(m_achunk);
|
ChunkList* cl = dynamic_cast<ChunkList*>(m_achunk);
|
||||||
|
|
@ -734,11 +780,11 @@ Chunk& Chunk::subChunkRef(unsigned int a) const
|
||||||
{
|
{
|
||||||
if(m_parent != nullptr)
|
if(m_parent != nullptr)
|
||||||
{
|
{
|
||||||
throw file_format_error("List size is below " + std::to_string(a), m_parent->filePath(), m_parent->c_data(), m_offset );
|
throw format_error("List size is below " + std::to_string(a), m_parent->filePath(), m_parent->stringdata(), m_offset );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw chunk_format_error("List size is below " + std::to_string(a), m_offset );
|
throw format_error("List size is below " + std::to_string(a), "", this->strval(), -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return *cl->list[a];
|
return *cl->list[a];
|
||||||
|
|
|
||||||
|
|
@ -6,25 +6,23 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#define KILL_COMMAND_FH "kill -s INT $(ps -f | grep \"aseqdump -p "
|
|
||||||
#define KILL_COMMAND_SH "\" | grep -v grep | awk '{print $2}' | head -n1)"
|
|
||||||
|
|
||||||
std::vector<Device*> device_list;
|
std::vector<Device*> device_list;
|
||||||
|
|
||||||
static void sh(std::string const& string)
|
|
||||||
{
|
|
||||||
system(string.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _isNum(char a)
|
static bool _isNum(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;
|
||||||
nb_command=0;
|
nb_command=0;
|
||||||
|
client_id=-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::~Device()
|
Device::~Device()
|
||||||
|
|
@ -247,9 +245,10 @@ void Device::run_signal(char* buff)
|
||||||
{
|
{
|
||||||
if ( (strstr(buff, "Port unsubscribed") != NULL) ) // distonnected
|
if ( (strstr(buff, "Port unsubscribed") != NULL) ) // distonnected
|
||||||
{
|
{
|
||||||
std::string kill_command=KILL_COMMAND_FH + this->name + KILL_COMMAND_SH;
|
std::string kill_command=KILL_COMMAND_FH + std::to_string(this->client_id) + KILL_COMMAND_SH;
|
||||||
system(kill_command.c_str()); // kill the process
|
system(kill_command.c_str()); // kill the process
|
||||||
this->busy=false;
|
this->busy=false;
|
||||||
|
this->client_id=-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (index(buff, ':') != NULL) // MIDI command
|
else if (index(buff, ':') != NULL) // MIDI command
|
||||||
|
|
@ -391,7 +390,7 @@ void Device::run_signal(char* buff)
|
||||||
result=(value-it.min)*(it.mapMax-it.mapMin)/(it.max-it.min)+it.mapMin;
|
result=(value-it.min)*(it.mapMax-it.mapMin)/(it.max-it.min)+it.mapMin;
|
||||||
|
|
||||||
//command execution
|
//command execution
|
||||||
std::string command=";channel=" + std::to_string(channel)
|
std::string command="channel=" + std::to_string(channel)
|
||||||
+ ";rawvalue=" + std::to_string(value)
|
+ ";rawvalue=" + std::to_string(value)
|
||||||
+ ";value=";
|
+ ";value=";
|
||||||
if(it.floating)
|
if(it.floating)
|
||||||
|
|
@ -412,7 +411,7 @@ void Device::run_signal(char* buff)
|
||||||
|
|
||||||
void Device::loop(Device* dev)
|
void Device::loop(Device* dev)
|
||||||
{
|
{
|
||||||
std::string command = "aseqdump -p '" + dev->name + '\'';
|
std::string command = "aseqdump -p '" + std::to_string(dev->client_id) + '\'';
|
||||||
FILE *stream = popen(command.c_str(), "r");
|
FILE *stream = popen(command.c_str(), "r");
|
||||||
char* buff = NULL;
|
char* buff = NULL;
|
||||||
size_t buff_size = 0;
|
size_t buff_size = 0;
|
||||||
|
|
|
||||||
145
src/main.cpp
145
src/main.cpp
|
|
@ -1,5 +1,6 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "device.hpp"
|
#include "device.hpp"
|
||||||
#include "system.hpp"
|
#include "system.hpp"
|
||||||
|
|
@ -7,30 +8,127 @@
|
||||||
#include "Filedat.hpp"
|
#include "Filedat.hpp"
|
||||||
#include "options.hpp"
|
#include "options.hpp"
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
#include "help.h"
|
||||||
|
|
||||||
|
OptionSet options;
|
||||||
|
|
||||||
|
void help()
|
||||||
|
{
|
||||||
|
printf("zmidimap [options] <midimap file>\n\nOptions:\n");
|
||||||
|
options.printHelp(2, 25);
|
||||||
|
printf("\nSee --file-format --command-tags --shell-format options for details on map file format\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void option_p(const std::string& port)
|
||||||
|
{
|
||||||
|
std::string command="aseqdump -p '" + port + '\'';
|
||||||
|
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 + port + KILL_COMMAND_SH;
|
||||||
|
system(kill_command.c_str()); // kill the process
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("%s", buff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup()
|
||||||
|
{
|
||||||
|
for(auto it : device_list)
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop(int ret)
|
||||||
|
{
|
||||||
|
exit(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void inthandler(int dummy)
|
||||||
|
{
|
||||||
|
stop(0);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
signal(SIGINT, inthandler);
|
||||||
|
signal(SIGCHLD, SIG_IGN); //not expecting returns from child processes
|
||||||
|
|
||||||
signal(SIGCHLD, SIG_IGN); //signal that we aren't expecting returns from child processes
|
options.addOption(Option('h',"help", false, "Display this help message"));
|
||||||
|
options.addOption(Option("file-format", false, "Display file format help"));
|
||||||
OptionSet options;
|
options.addOption(Option("command-tags", false, "Display for command tag help"));
|
||||||
options.addOption(Option('f',"file",true));
|
options.addOption(Option("shell-format", false, "Display for shell format help"));
|
||||||
|
options.addOption(Option('l',"list", false, "List detected devices"));
|
||||||
|
options.addOption(Option('L',"full-list", false, "Print whole device list details"));
|
||||||
|
options.addOption(Option('p',"port", true, "Connect to device and output to console", "device"));
|
||||||
|
// options.addOption(Option('i',"interactive", false, "Start in interactive mode"));
|
||||||
|
|
||||||
auto argvec = argVector(argc, argv);
|
auto argvec = argVector(argc, argv);
|
||||||
|
|
||||||
auto t = options.getOptions(argvec);
|
auto t = options.getOptions(argvec);
|
||||||
std::vector<std::string> arg=t.first;
|
std::vector<std::string> arg=t.first;
|
||||||
if( !t.second )
|
if( !t.second ) //invalid option
|
||||||
{
|
|
||||||
fprintf(stderr, "Unexpected error\n");
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
Option* op=nullptr;
|
||||||
|
op = options.findOption('h');
|
||||||
|
if( op->activated )
|
||||||
|
{
|
||||||
|
help();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
op = options.findOption("file-format");
|
||||||
|
if( op->activated )
|
||||||
|
{
|
||||||
|
printf("%s\n", FILE_FORMAT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
op = options.findOption("command-tags");
|
||||||
|
if( op->activated )
|
||||||
|
{
|
||||||
|
printf("%s\n", COMMAND_TAGS);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
op = options.findOption("shell-format");
|
||||||
|
if( op->activated )
|
||||||
|
{
|
||||||
|
printf("%s\n", SHELL_FORMAT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
op = options.findOption('h');
|
||||||
|
if( op->activated )
|
||||||
|
{
|
||||||
|
help();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
op = options.findOption('L');
|
||||||
|
if( op->activated )
|
||||||
|
{
|
||||||
|
sh("aseqdump -l");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
op = options.findOption('l');
|
||||||
|
if( op->activated )
|
||||||
|
{
|
||||||
|
sh(LIST_COMMAND);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
op = options.findOption('p');
|
||||||
|
if( op->activated )
|
||||||
|
{
|
||||||
|
option_p(op->argument);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg.size() <= 0 || arg[0] == "")
|
if (arg.size() <= 0 || arg[0] == "")
|
||||||
{
|
{
|
||||||
fprintf(stderr, "No config file specified\n");
|
help();
|
||||||
return 2;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Filedat file(arg[0]);
|
Filedat file(arg[0]);
|
||||||
|
|
@ -40,11 +138,12 @@ int main(int argc, char* argv[])
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Loading config file '%s'\n", arg[0].c_str());
|
printf("Loading map file '%s'\n", arg[0].c_str());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
file.importFile();
|
file.import_file();
|
||||||
|
|
||||||
|
//create commands
|
||||||
for(int i=0 ; i<file.chunk().listSize() ; i++)
|
for(int i=0 ; i<file.chunk().listSize() ; i++)
|
||||||
{
|
{
|
||||||
Device *newDevice = new Device;
|
Device *newDevice = new Device;
|
||||||
|
|
@ -53,30 +152,22 @@ int main(int argc, char* argv[])
|
||||||
printf("Loaded %d commands for device '%s'\n", newDevice->nb_command, newDevice->name.c_str());
|
printf("Loaded %d commands for device '%s'\n", newDevice->nb_command, newDevice->name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//main loop
|
||||||
printf("Starting scan for devices\n");
|
printf("Starting scan for devices\n");
|
||||||
announce_loop();
|
announce_loop();
|
||||||
|
|
||||||
for(auto it : device_list)
|
|
||||||
{
|
|
||||||
delete it;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (file_format_error& e)
|
catch (format_error& e)
|
||||||
{
|
{
|
||||||
printErrorIndex(e.data(), e.where(), e.what(), e.origin());
|
printFormatException(e);
|
||||||
return 11;
|
cleanup();
|
||||||
}
|
stop(11);
|
||||||
catch (chunk_format_error& e)
|
|
||||||
{
|
|
||||||
std::cerr << "Chunk Error: " << e.what() << std::endl ;
|
|
||||||
return 11;
|
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
std::cerr << "Exception: " << e.what() << std::endl;
|
std::cerr << "Exception: " << e.what() << std::endl;
|
||||||
return 11;
|
stop(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanup();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,16 +24,18 @@ Option::~Option()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Option::Option(char c, bool arg)
|
Option::Option(char c, bool arg, std::string helptext, std::string argname)
|
||||||
{
|
{
|
||||||
shortDef=true;
|
shortDef=true;
|
||||||
longDef=false;
|
longDef=false;
|
||||||
takesArgument=arg;
|
takesArgument=arg;
|
||||||
activated=false;
|
activated=false;
|
||||||
charName=c;
|
charName=c;
|
||||||
|
arg_name=argname;
|
||||||
|
help_text=helptext;
|
||||||
}
|
}
|
||||||
|
|
||||||
Option::Option(std::string const& str, bool arg)
|
Option::Option(std::string const& str, bool arg, std::string helptext, std::string argname)
|
||||||
{
|
{
|
||||||
shortDef=false;
|
shortDef=false;
|
||||||
longDef=true;
|
longDef=true;
|
||||||
|
|
@ -41,8 +43,10 @@ Option::Option(std::string const& str, bool arg)
|
||||||
activated=false;
|
activated=false;
|
||||||
charName=0;
|
charName=0;
|
||||||
strName=str;
|
strName=str;
|
||||||
|
arg_name=argname;
|
||||||
|
help_text=helptext;
|
||||||
}
|
}
|
||||||
Option::Option(char c, std::string const& str, bool arg)
|
Option::Option(char c, std::string const& str, bool arg, std::string helptext, std::string argname)
|
||||||
{
|
{
|
||||||
shortDef=true;
|
shortDef=true;
|
||||||
longDef=true;
|
longDef=true;
|
||||||
|
|
@ -50,6 +54,47 @@ Option::Option(char c, std::string const& str, bool arg)
|
||||||
activated=false;
|
activated=false;
|
||||||
charName=c;
|
charName=c;
|
||||||
strName=str;
|
strName=str;
|
||||||
|
arg_name=argname;
|
||||||
|
help_text=helptext;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Option::printHelp(int leftpad, int rightpad)
|
||||||
|
{
|
||||||
|
//prepadding
|
||||||
|
printf("%*s", -1*leftpad, "");
|
||||||
|
|
||||||
|
//short def
|
||||||
|
if(this->shortDef)
|
||||||
|
{
|
||||||
|
printf("-%c ", this->charName);
|
||||||
|
rightpad -= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
//longdef
|
||||||
|
if(this->longDef)
|
||||||
|
{
|
||||||
|
printf("--%s ", this->strName.c_str());
|
||||||
|
rightpad -= 3 + this->strName.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
//argument
|
||||||
|
if(this->takesArgument)
|
||||||
|
{
|
||||||
|
printf(" <%s>", arg_name.c_str());
|
||||||
|
rightpad -= arg_name.size() + 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%*s%s", -1*rightpad, "", help_text.c_str());
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptionSet::printHelp(int leftpad, int rightpad)
|
||||||
|
{
|
||||||
|
for(auto it : this->m_options)
|
||||||
|
{
|
||||||
|
it.printHelp(leftpad,rightpad);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OptionSet::OptionSet()
|
OptionSet::OptionSet()
|
||||||
|
|
@ -101,9 +146,9 @@ std::pair<std::vector<std::string>, bool> OptionSet::getOptions(std::vector<std:
|
||||||
}
|
}
|
||||||
if(popt->takesArgument)
|
if(popt->takesArgument)
|
||||||
{
|
{
|
||||||
if( ++it == input.end() ) //se termine ici
|
if( ++it == input.end() ) //finishes here
|
||||||
{
|
{
|
||||||
(*errStream) << "No argument given to option " << popt->strName << std::endl;
|
(*errStream) << "No argument given to option --" << popt->strName << std::endl;
|
||||||
return std::make_pair(out, false);
|
return std::make_pair(out, false);
|
||||||
}
|
}
|
||||||
popt->activated = true;
|
popt->activated = true;
|
||||||
|
|
@ -138,17 +183,17 @@ std::pair<std::vector<std::string>, bool> OptionSet::getOptions(std::vector<std:
|
||||||
while( !tstop && it!=input.end() && (*it).size()>i )
|
while( !tstop && it!=input.end() && (*it).size()>i )
|
||||||
{
|
{
|
||||||
popt=this->findOption((*it)[i]);
|
popt=this->findOption((*it)[i]);
|
||||||
if(popt==nullptr) //non trouvé: erreur
|
if(popt==nullptr) //not found: error
|
||||||
{
|
{
|
||||||
(*errStream) << "Unknown option: " << (*it)[i] << std::endl;
|
(*errStream) << "Unknown option: -" << (*it)[i] << std::endl;
|
||||||
return std::make_pair(out, false);
|
return std::make_pair(out, false);
|
||||||
}
|
}
|
||||||
if(popt->takesArgument) //prends un argument
|
if(popt->takesArgument) //no argument
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
if((*it).size()<=i) //se termine ici
|
if((*it).size()<=i) //finishes here
|
||||||
{
|
{
|
||||||
if( ++it == input.end() ) //se termine ici
|
if( ++it == input.end() )
|
||||||
{
|
{
|
||||||
(*errStream) << "No argument given to option -" << popt->charName << std::endl;
|
(*errStream) << "No argument given to option -" << popt->charName << std::endl;
|
||||||
return std::make_pair(out, false);
|
return std::make_pair(out, false);
|
||||||
|
|
@ -170,7 +215,7 @@ std::pair<std::vector<std::string>, bool> OptionSet::getOptions(std::vector<std:
|
||||||
tstop=true;
|
tstop=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // ne prends pas d'argument
|
else //no argument
|
||||||
{
|
{
|
||||||
popt->activated = true;
|
popt->activated = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,28 +9,50 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define ANNOUNCE_COMMAND "aseqdump -p System:1"
|
|
||||||
#define LIST_COMMAND "aseqdump -l | tail -n +2 | cut -c10- | tr -s ' '"
|
|
||||||
|
|
||||||
void device_check()
|
void device_check()
|
||||||
{
|
{
|
||||||
char* buff = NULL;
|
char* buff = NULL;
|
||||||
size_t buff_size = 0;
|
size_t buff_size = 0;
|
||||||
FILE *stream = popen(LIST_COMMAND, "r");
|
FILE *stream = popen(LIST_EXTENDED_COMMAND, "r");
|
||||||
std::string str;
|
std::string str;
|
||||||
|
std::vector<std::pair<int,std::string>> ls_device;
|
||||||
|
|
||||||
getline(&buff, &buff_size, stream); //discard the first line
|
getline(&buff, &buff_size, stream); //discard the first line
|
||||||
|
int i=0,j=0;
|
||||||
|
int t;
|
||||||
while ( getline(&buff, &buff_size, stream) > 0 ) //retrieve device lines
|
while ( getline(&buff, &buff_size, stream) > 0 ) //retrieve device lines
|
||||||
{
|
{
|
||||||
str += buff;
|
//port id get
|
||||||
|
i=0;
|
||||||
|
while(buff[i] == ' ')
|
||||||
|
i++;
|
||||||
|
j=i;
|
||||||
|
while(buff[i] != ':')
|
||||||
|
i++;
|
||||||
|
t=stoi( std::string(buff+j, i-j) );
|
||||||
|
//name get
|
||||||
|
j=9;
|
||||||
|
i=10;
|
||||||
|
while(buff[i+1] != '\n')
|
||||||
|
i++;
|
||||||
|
while(buff[i-1] == ' ')
|
||||||
|
i--;
|
||||||
|
|
||||||
|
ls_device.push_back(std::make_pair(t, std::string(buff+j, i-j)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( auto it : device_list ) // iterate devices
|
for ( auto dev : device_list ) // iterate devices
|
||||||
{
|
{
|
||||||
if( !it->busy && str.find(it->name) != std::string::npos ) //device detected
|
for ( auto ls = ls_device.begin() ; ls != ls_device.end() ; ls++ )
|
||||||
{
|
{
|
||||||
printf("Device '%s' found\n", it->name.c_str());
|
if( !dev->busy && dev->name == ls->second && ls->first > 0) //device detected
|
||||||
it->start_loop();
|
{
|
||||||
|
dev->client_id = ls->first;
|
||||||
|
printf("Device '%s' found\n", dev->name.c_str());
|
||||||
|
dev->start_loop();
|
||||||
|
}
|
||||||
|
if( dev->busy && dev->client_id == ls->first )
|
||||||
|
ls->first = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue