# HG changeset patch # User František Kučera # Date 1602027930 -7200 # Node ID 07247893054ea3694054ca474fcd3dbdc917c6f3 # Parent ded44e94147cd7dceb5a8fb072f61c5d7e26842c add options --list-jack-ports and --list-midi-messages useful for bash-completion diff -r ded44e94147c -r 07247893054e bash-completion.sh --- 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 . +_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 diff -r ded44e94147c -r 07247893054e src/CLIParser.h --- 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& 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"; } } diff -r ded44e94147c -r 07247893054e src/Configuration.h --- 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 portsToConnect; int requiredJackConnections = 0; + relpipe::common::type::Boolean listJackPorts = false; + relpipe::common::type::Boolean listMidiMessages = true; virtual ~Configuration() { } diff -r ded44e94147c -r 07247893054e src/JackCommand.h --- 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 #include #include +#include #include #include @@ -171,6 +172,50 @@ return result.str(); } + void listPorts(std::shared_ptr writer) { + using namespace relpipe::writer; + vector 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> 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 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}); diff -r ded44e94147c -r 07247893054e src/relpipe-in-jack.cpp --- 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 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());