# HG changeset patch # User František Kučera # Date 1601762668 -7200 # Node ID b3239e4ad328b5a13c23a841153d8959913ba776 # Parent 9ad606c80d3bbb775a6160f9eae41507fc96d566 connect to configured destination ports + syncCallback diff -r 9ad606c80d3b -r b3239e4ad328 bash-completion.sh --- a/bash-completion.sh Sat Oct 03 18:20:30 2020 +0200 +++ b/bash-completion.sh Sun Oct 04 00:04:28 2020 +0200 @@ -13,6 +13,24 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +_relpipe_out_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' 'input' \ + | relpipe-tr-grep '.*' 'type' 'midi' \ + | relpipe-tr-cut '.*' 'name' \ + | relpipe-out-nullbyte \ + | tr \\0 \\n +} + _relpipe_out_jack_completion() { local w0 w1 w2 w3 @@ -22,17 +40,13 @@ w2=${COMP_WORDS[COMP_CWORD-2]} w3=${COMP_WORDS[COMP_CWORD-3]} - BOOLEAN=( - "true" - "false" - ) - - if [[ "$w1" == "--TODO" ]]; then COMPREPLY=($(compgen -W "${BOOLEAN[*]}" -- "$w0")) - elif [[ "$w1" == "--jack-client-name" && "x$w0" == "x" ]]; then COMPREPLY=("'relpipe-out-jack'") + if [[ "$w1" == "--jack-client-name" && "x$w0" == "x" ]]; then COMPREPLY=("'relpipe-out-jack'") + elif [[ "$w1" == "--jack-connect-to-port" ]]; then COMPREPLY=($(compgen -W "$(_relpipe_out_jack_completion_ports)" -- "$w0")) elif [[ "$w1" == "--required-jack-connections" && "x$w0" == "x" ]]; then COMPREPLY=("1") else OPTIONS=( "--jack-client-name" + "--jack-connect-to-port" "--required-jack-connections" ) COMPREPLY=($(compgen -W "${OPTIONS[*]}" -- "$w0")) diff -r 9ad606c80d3b -r b3239e4ad328 src/CLIParser.h --- a/src/CLIParser.h Sat Oct 03 18:20:30 2020 +0200 +++ b/src/CLIParser.h Sun Oct 04 00:04:28 2020 +0200 @@ -49,6 +49,7 @@ public: 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; Configuration parse(const std::vector& arguments) { @@ -59,6 +60,8 @@ if (option == OPTION_JACK_CLIENT_NAME) { c.jackClientName = readNext(arguments, i); + } else if (option == OPTION_JACK_CONNECT_TO_PORT) { + c.portsToConnect.push_back(readNext(arguments, i)); } else if (option == OPTION_REQUIRED_JACK_CONNECTIONS) { c.requiredJackConnections = std::stoi(readNext(arguments, i)); } else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS); @@ -72,6 +75,7 @@ }; 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"; } diff -r 9ad606c80d3b -r b3239e4ad328 src/Configuration.h --- a/src/Configuration.h Sat Oct 03 18:20:30 2020 +0200 +++ b/src/Configuration.h Sun Oct 04 00:04:28 2020 +0200 @@ -29,6 +29,7 @@ class Configuration { public: relpipe::common::type::StringX jackClientName = L"relpipe-out-jack"; + std::vector portsToConnect; int requiredJackConnections = 1; virtual ~Configuration() { diff -r 9ad606c80d3b -r b3239e4ad328 src/JackHandler.h --- a/src/JackHandler.h Sat Oct 03 18:20:30 2020 +0200 +++ b/src/JackHandler.h Sun Oct 04 00:04:28 2020 +0200 @@ -89,8 +89,17 @@ return 0; } - static int processCallback(jack_nframes_t frames, void* arg) { - return static_cast (arg)->processCallback(frames); + int syncCallback(jack_transport_state_t state, jack_position_t* position) { + std::wcerr << L"syncCallback()" << std::endl; + return true; + } + + static int processCallback(jack_nframes_t frames, void* instance) { + return static_cast (instance)->processCallback(frames); + } + + static int syncCallback(jack_transport_state_t state, jack_position_t* position, void* instance) { + return static_cast (instance)->syncCallback(state, position); } } realTimeContext; @@ -161,6 +170,7 @@ realTimeContext.ringBuffer = jack_ringbuffer_create(realTimeContext.RING_BUFFER_SIZE * sizeof (MidiMessage)); jack_set_process_callback(realTimeContext.jackClient, RealTimeContext::processCallback, &realTimeContext); + jack_set_sync_callback(realTimeContext.jackClient, RealTimeContext::syncCallback, &realTimeContext); // TODO: report also other events (connections etc.) jack_set_error_function(jackErrorCallback); jack_set_info_function(jackErrorCallback); @@ -173,10 +183,18 @@ int jackError = jack_activate(realTimeContext.jackClient); if (jackError) throw JackException(L"Could not activate client."); + + // Connect to configured destination ports: + const char* jackPortName = jack_port_name(realTimeContext.jackPort); + for (auto destinationPort : configuration.portsToConnect) { + int error = jack_connect(realTimeContext.jackClient, jackPortName, convertor.to_bytes(destinationPort).c_str()); + if (error) throw JackException(L"Connection to the JACK port failed: " + destinationPort); + } + // Wait for a port connection, because it does not make much sense to send MIDI events nowhere: while (jack_port_connected(realTimeContext.jackPort) < configuration.requiredJackConnections) usleep(10000); - // TODO: configurable auto-connection to another client/port + // TODO: finalize on exceptions } void startRelation(const relpipe::common::type::StringX name, std::vector attributes) override {