add options --list-jack-ports and --list-midi-messages useful for bash-completion
--- a/bash-completion.sh Tue Oct 06 16:55:22 2020 +0200
+++ b/bash-completion.sh Wed Oct 07 01:45:30 2020 +0200
@@ -13,22 +13,15 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+_relpipe_in_jack_completion_read_nullbyte() { local IFS=; for v in "$@"; do export "$v"; read -r -d '' "$v"; done }
+
_relpipe_in_jack_completion_ports() {
- # TODO: simpler and faster implementation in relpipe-out-jack or relpipe-in-jack C++ code
- jack_lsp -tp 2>/dev/null \
- | tr -d \\t \
- | tr \\n \\0 \
- | relpipe-in-cli \
- --relation "jack_midi_port" \
- --attribute "name" string \
- --attribute "properties" string \
- --attribute "type" string \
- --records-on-stdin true \
- | relpipe-tr-grep '.*' 'properties' 'output' \
- | relpipe-tr-grep '.*' 'type' 'midi' \
- | relpipe-tr-cut '.*' 'name' \
- | relpipe-out-nullbyte \
- | tr \\0 \\n
+ if type relpipe-in-jack &> /dev/null && type relpipe-out-nullbyte &> /dev/null; then
+ relpipe-in-jack --list-jack-ports true --list-midi-messages false 2>/dev/null \
+ | relpipe-out-nullbyte \
+ | while _relpipe_in_jack_completion_read_nullbyte "name" "input" "output" "physical" "terminal" "mine" "midi" "type"; do
+ if [[ "$midi" = "true" && "$output" = "true" && "$mine" = "false" ]]; then echo "$name"; fi; done
+ fi
}
_relpipe_in_jack_completion() {
@@ -40,14 +33,23 @@
w2=${COMP_WORDS[COMP_CWORD-2]}
w3=${COMP_WORDS[COMP_CWORD-3]}
+ BOOLEAN_VALUES=(
+ "true"
+ "false"
+ )
+
if [[ "$w1" == "--jack-client-name" && "x$w0" == "x" ]]; then COMPREPLY=("'relpipe-in-jack'")
elif [[ "$w1" == "--jack-connect-to-port" ]]; then COMPREPLY=($(compgen -W "$(_relpipe_in_jack_completion_ports)" -- "$w0"))
elif [[ "$w1" == "--required-jack-connections" && "x$w0" == "x" ]]; then COMPREPLY=("0")
+ elif [[ "$w1" == "--list-jack-ports" ]]; then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
+ elif [[ "$w1" == "--list-midi-messages" ]]; then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0"))
else
OPTIONS=(
"--jack-client-name"
"--jack-connect-to-port"
"--required-jack-connections"
+ "--list-jack-ports"
+ "--list-midi-messages"
)
COMPREPLY=($(compgen -W "${OPTIONS[*]}" -- "$w0"))
fi
--- a/src/CLIParser.h Tue Oct 06 16:55:22 2020 +0200
+++ b/src/CLIParser.h Wed Oct 07 01:45:30 2020 +0200
@@ -51,6 +51,8 @@
static const relpipe::common::type::StringX OPTION_JACK_CLIENT_NAME;
static const relpipe::common::type::StringX OPTION_JACK_CONNECT_TO_PORT;
static const relpipe::common::type::StringX OPTION_REQUIRED_JACK_CONNECTIONS;
+ static const relpipe::common::type::StringX OPTION_LIST_JACK_PORTS;
+ static const relpipe::common::type::StringX OPTION_LIST_MIDI_MESSAGES;
Configuration parse(const std::vector<relpipe::common::type::StringX>& arguments) {
Configuration c;
@@ -64,6 +66,10 @@
c.portsToConnect.push_back(readNext(arguments, i));
} else if (option == OPTION_REQUIRED_JACK_CONNECTIONS) {
c.requiredJackConnections = std::stoi(readNext(arguments, i));
+ } else if (option == OPTION_LIST_JACK_PORTS) {
+ c.listJackPorts = parseBoolean(readNext(arguments, i));
+ } else if (option == OPTION_LIST_MIDI_MESSAGES) {
+ c.listMidiMessages = parseBoolean(readNext(arguments, i));
} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
}
@@ -77,6 +83,8 @@
const relpipe::common::type::StringX CLIParser::OPTION_JACK_CLIENT_NAME = L"--jack-client-name";
const relpipe::common::type::StringX CLIParser::OPTION_JACK_CONNECT_TO_PORT = L"--jack-connect-to-port";
const relpipe::common::type::StringX CLIParser::OPTION_REQUIRED_JACK_CONNECTIONS = L"--required-jack-connections";
+const relpipe::common::type::StringX CLIParser::OPTION_LIST_JACK_PORTS = L"--list-jack-ports";
+const relpipe::common::type::StringX CLIParser::OPTION_LIST_MIDI_MESSAGES = L"--list-midi-messages";
}
}
--- a/src/Configuration.h Tue Oct 06 16:55:22 2020 +0200
+++ b/src/Configuration.h Wed Oct 07 01:45:30 2020 +0200
@@ -28,9 +28,17 @@
class Configuration {
public:
+
+ enum class PortType {
+ MIDI_INPUT,
+ MIDI_OUTPUT
+ };
+
relpipe::common::type::StringX jackClientName = L"relpipe-in-jack";
std::vector<relpipe::common::type::StringX> portsToConnect;
int requiredJackConnections = 0;
+ relpipe::common::type::Boolean listJackPorts = false;
+ relpipe::common::type::Boolean listMidiMessages = true;
virtual ~Configuration() {
}
--- a/src/JackCommand.h Tue Oct 06 16:55:22 2020 +0200
+++ b/src/JackCommand.h Wed Oct 07 01:45:30 2020 +0200
@@ -26,6 +26,7 @@
#include <pthread.h>
#include <functional>
#include <iomanip>
+#include <regex>
#include <jack/jack.h>
#include <jack/midiport.h>
@@ -171,6 +172,50 @@
return result.str();
}
+ void listPorts(std::shared_ptr<relpipe::writer::RelationalWriter> writer) {
+ using namespace relpipe::writer;
+ vector<AttributeMetadata> metadata;
+
+ metadata.push_back({L"name", TypeId::STRING});
+ metadata.push_back({L"input", TypeId::BOOLEAN});
+ metadata.push_back({L"output", TypeId::BOOLEAN});
+ metadata.push_back({L"physical", TypeId::BOOLEAN});
+ metadata.push_back({L"terminal", TypeId::BOOLEAN});
+ metadata.push_back({L"mine", TypeId::BOOLEAN});
+ metadata.push_back({L"midi", TypeId::BOOLEAN});
+ metadata.push_back({L"type", TypeId::STRING});
+ writer->startRelation(L"port", metadata, true);
+
+ const char** portNames = jack_get_ports(realTimeContext.jackClient, nullptr, nullptr, 0);
+
+ std::regex midiTypePattern(".*midi$");
+
+ for (const char** portName = portNames; *portName; portName++) {
+ jack_port_t* port = jack_port_by_name(realTimeContext.jackClient, *portName);
+
+ const char* portType = jack_port_type(port);
+ int portFlags = jack_port_flags(port);
+
+ bool isInput = portFlags & JackPortFlags::JackPortIsInput;
+ bool isOuputput = portFlags & JackPortFlags::JackPortIsOutput;
+ bool isPhysical = portFlags & JackPortFlags::JackPortIsPhysical;
+ bool isTerminal = portFlags & JackPortFlags::JackPortIsTerminal;
+ bool isMine = jack_port_is_mine(realTimeContext.jackClient, port);
+ bool isMidi = std::regex_search(portType, midiTypePattern);
+
+ writer->writeAttribute(convertor.from_bytes(*portName));
+ writer->writeAttribute(&isInput, typeid (isInput));
+ writer->writeAttribute(&isOuputput, typeid (isOuputput));
+ writer->writeAttribute(&isPhysical, typeid (isPhysical));
+ writer->writeAttribute(&isTerminal, typeid (isTerminal));
+ writer->writeAttribute(&isMine, typeid (isMine));
+ writer->writeAttribute(&isMidi, typeid (isMidi));
+ writer->writeAttribute(convertor.from_bytes(portType));
+ }
+
+ jack_free(portNames);
+ }
+
static void jackErrorCallback(const char * message) {
std::wstring_convert < std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding
std::wcerr << L"JACK: " << convertor.from_bytes(message) << std::endl;
@@ -235,6 +280,10 @@
// Relation headers:
using namespace relpipe::writer;
vector<AttributeMetadata> metadata;
+
+ if (configuration.listJackPorts) listPorts(writer);
+ if (!configuration.listMidiMessages) return;
+
metadata.push_back({L"event", TypeId::STRING});
metadata.push_back({L"channel", TypeId::INTEGER});
metadata.push_back({L"note_on", TypeId::BOOLEAN});
--- a/src/relpipe-in-jack.cpp Tue Oct 06 16:55:22 2020 +0200
+++ b/src/relpipe-in-jack.cpp Wed Oct 07 01:45:30 2020 +0200
@@ -40,7 +40,6 @@
setlocale(LC_ALL, "");
CLI::untieStdIO();
CLI cli(argc, argv);
- // TODO: options, CLI parsing, configurable attributes
int resultCode = CLI::EXIT_CODE_UNEXPECTED_ERROR;
try {
@@ -52,6 +51,10 @@
std::shared_ptr<RelationalWriter> writer(Factory::create(std::cout));
jackCommand->processJackStream(writer, std::bind(fflush, stdout)); // std::bind(fflush, XXX) Factory::create(XXX) must be the same stream XXX
resultCode = CLI::EXIT_CODE_SUCCESS;
+ } catch (RelpipeCLIException e) {
+ fwprintf(stderr, L"Caught CLI exception: %ls\n", e.getMessge().c_str());
+ fwprintf(stderr, L"Debug: Input stream: eof=%ls, lastRead=%d\n", (cin.eof() ? L"true" : L"false"), cin.gcount());
+ resultCode = e.getExitCode();
} catch (JackException e) {
fwprintf(stderr, L"Caught JACK exception: %ls\n", e.getMessge().c_str());
fwprintf(stderr, L"Debug: Input stream: eof=%ls, lastRead=%d\n", (cin.eof() ? L"true" : L"false"), cin.gcount());