diff options
author | David Robillard <d@drobilla.net> | 2018-11-24 13:44:03 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2018-11-24 13:44:03 +0100 |
commit | a7d83f19b08eb4c6f79a82fe60c2b86db13f4420 (patch) | |
tree | d9b620bfba1e7462df4ddb3f6225cc5216c0ca81 /src/server/JackDriver.cpp | |
parent | d63edc742cebd685f8a05936682210aa5c1e69a9 (diff) | |
download | ingen-a7d83f19b08eb4c6f79a82fe60c2b86db13f4420.tar.gz ingen-a7d83f19b08eb4c6f79a82fe60c2b86db13f4420.tar.bz2 ingen-a7d83f19b08eb4c6f79a82fe60c2b86db13f4420.zip |
Squashed 'waflib/' changes from 6e726eb1..5ea8f99f
5ea8f99f Improve test output spacing
0e23b29f Raise exception when test suite fails to ensure non-zero exit status
d6de073b Show run time of unit tests
5b655541 Add short configure option for ultra-strict flags
4687ba6d Use gtest-like test output
258903d9 Fix failure count in test group summaries
da07e738 Fix verbose tests with Python 3
git-subtree-dir: waflib
git-subtree-split: 5ea8f99f6e1246079c1fe6bb590c38a53aadd40d
Diffstat (limited to 'src/server/JackDriver.cpp')
-rw-r--r-- | src/server/JackDriver.cpp | 584 |
1 files changed, 0 insertions, 584 deletions
diff --git a/src/server/JackDriver.cpp b/src/server/JackDriver.cpp deleted file mode 100644 index 973e3eb7..00000000 --- a/src/server/JackDriver.cpp +++ /dev/null @@ -1,584 +0,0 @@ -/* - This file is part of Ingen. - Copyright 2007-2017 David Robillard <http://drobilla.net/> - - Ingen is free software: you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free - Software Foundation, either version 3 of the License, or any later version. - - Ingen is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. - - You should have received a copy of the GNU Affero General Public License - along with Ingen. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "ingen_config.h" - -#include <cstdlib> -#include <string> - -#include <jack/midiport.h> -#ifdef INGEN_JACK_SESSION -#include <jack/session.h> -#include <boost/format.hpp> -#include "ingen/Serialiser.hpp" -#endif -#ifdef HAVE_JACK_METADATA -#include <jack/metadata.h> -#include "jackey.h" -#endif - -#include "ingen/Configuration.hpp" -#include "ingen/LV2Features.hpp" -#include "ingen/Log.hpp" -#include "ingen/URI.hpp" -#include "ingen/URIMap.hpp" -#include "ingen/World.hpp" -#include "lv2/lv2plug.in/ns/ext/atom/util.h" - -#include "Buffer.hpp" -#include "DuplexPort.hpp" -#include "Engine.hpp" -#include "GraphImpl.hpp" -#include "JackDriver.hpp" -#include "PortImpl.hpp" -#include "ThreadManager.hpp" -#include "util.hpp" - -typedef jack_default_audio_sample_t jack_sample_t; - -namespace Ingen { -namespace Server { - -JackDriver::JackDriver(Engine& engine) - : _engine(engine) - , _sem(0) - , _flag(false) - , _client(nullptr) - , _block_length(0) - , _seq_size(0) - , _sample_rate(0) - , _is_activated(false) - , _old_bpm(120.0f) - , _old_frame(0) - , _old_rolling(false) -{ - _midi_event_type = _engine.world()->uris().midi_MidiEvent; - lv2_atom_forge_init( - &_forge, &engine.world()->uri_map().urid_map_feature()->urid_map); -} - -JackDriver::~JackDriver() -{ - deactivate(); - _ports.clear_and_dispose([](EnginePort* p) { delete p; }); -} - -bool -JackDriver::attach(const std::string& server_name, - const std::string& client_name, - void* jack_client) -{ - assert(!_client); - if (!jack_client) { -#ifdef INGEN_JACK_SESSION - const std::string uuid = _engine.world()->jack_uuid(); - if (!uuid.empty()) { - _client = jack_client_open(client_name.c_str(), - JackSessionID, nullptr, - uuid.c_str()); - _engine.log().info(fmt("Connected to Jack as `%1%' (UUID `%2%')\n") - % client_name.c_str() % uuid); - } -#endif - - // Try supplied server name - if (!_client && !server_name.empty()) { - if ((_client = jack_client_open(client_name.c_str(), - JackServerName, nullptr, - server_name.c_str()))) { - _engine.log().info(fmt("Connected to Jack server `%1%'\n") - % server_name); - } - } - - // Either server name not specified, or supplied server name does not exist - // Connect to default server - if (!_client) { - if ((_client = jack_client_open(client_name.c_str(), JackNullOption, nullptr))) { - _engine.log().info("Connected to default Jack server\n"); - } - } - - // Still failed - if (!_client) { - _engine.log().error("Unable to connect to Jack\n"); - return false; - } - } else { - _client = (jack_client_t*)jack_client; - } - - _sample_rate = jack_get_sample_rate(_client); - _block_length = jack_get_buffer_size(_client); - _seq_size = jack_port_type_get_buffer_size(_client, JACK_DEFAULT_MIDI_TYPE); - - _fallback_buffer = AudioBufPtr( - static_cast<float*>( - Buffer::aligned_alloc(sizeof(float) * _block_length))); - - jack_on_shutdown(_client, shutdown_cb, this); - - jack_set_thread_init_callback(_client, thread_init_cb, this); - jack_set_buffer_size_callback(_client, block_length_cb, this); -#ifdef INGEN_JACK_SESSION - jack_set_session_callback(_client, session_cb, this); -#endif - - for (auto& p : _ports) { - register_port(p); - } - - return true; -} - -bool -JackDriver::activate() -{ - World* world = _engine.world(); - - if (_is_activated) { - _engine.log().warn("Jack driver already activated\n"); - return false; - } - - if (!_client) { - attach(world->conf().option("jack-server").ptr<char>(), - world->conf().option("jack-name").ptr<char>(), nullptr); - } - - if (!_client) { - return false; - } - - jack_set_process_callback(_client, process_cb, this); - - _is_activated = true; - - if (jack_activate(_client)) { - _engine.log().error("Could not activate Jack client, aborting\n"); - return false; - } else { - _engine.log().info(fmt("Activated Jack client `%1%'\n") % - world->conf().option("jack-name").ptr<char>()); - } - return true; -} - -void -JackDriver::deactivate() -{ - if (_is_activated) { - _flag = true; - _is_activated = false; - _sem.timed_wait(std::chrono::seconds(1)); - - for (auto& p : _ports) { - unregister_port(p); - } - - if (_client) { - jack_deactivate(_client); - jack_client_close(_client); - _client = nullptr; - } - - _engine.log().info("Deactivated Jack client\n"); - } -} - -EnginePort* -JackDriver::get_port(const Raul::Path& path) -{ - for (auto& p : _ports) { - if (p.graph_port()->path() == path) { - return &p; - } - } - - return nullptr; -} - -void -JackDriver::add_port(RunContext& context, EnginePort* port) -{ - _ports.push_back(*port); - - DuplexPort* graph_port = port->graph_port(); - if (graph_port->is_a(PortType::AUDIO) || graph_port->is_a(PortType::CV)) { - const SampleCount nframes = context.nframes(); - jack_port_t* jport = (jack_port_t*)port->handle(); - void* jbuf = jack_port_get_buffer(jport, nframes); - - /* Jack fails to return a buffer if this is too soon after registering - the port, so use a silent fallback buffer if necessary. */ - graph_port->set_driver_buffer( - jbuf ? jbuf : _fallback_buffer.get(), - nframes * sizeof(float)); - } -} - -void -JackDriver::remove_port(RunContext& context, EnginePort* port) -{ - _ports.erase(_ports.iterator_to(*port)); -} - -void -JackDriver::register_port(EnginePort& port) -{ - jack_port_t* jack_port = jack_port_register( - _client, - port.graph_port()->path().substr(1).c_str(), - ((port.graph_port()->is_a(PortType::AUDIO) || - port.graph_port()->is_a(PortType::CV)) - ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE), - (port.graph_port()->is_input() - ? JackPortIsInput : JackPortIsOutput), - 0); - - if (!jack_port) { - throw JackDriver::PortRegistrationFailedException(); - } - - port.set_handle(jack_port); - - for (const auto& p : port.graph_port()->properties()) { - port_property_internal(jack_port, p.first, p.second); - } -} - -void -JackDriver::unregister_port(EnginePort& port) -{ - if (jack_port_unregister(_client, (jack_port_t*)port.handle())) { - _engine.log().error("Failed to unregister Jack port\n"); - } - - port.set_handle(nullptr); -} - -void -JackDriver::rename_port(const Raul::Path& old_path, - const Raul::Path& new_path) -{ - EnginePort* eport = get_port(old_path); - if (eport) { -#ifdef HAVE_JACK_PORT_RENAME - jack_port_rename( - _client, (jack_port_t*)eport->handle(), new_path.substr(1).c_str()); -#else - jack_port_set_name((jack_port_t*)eport->handle(), - new_path.substr(1).c_str()); -#endif - } -} - -void -JackDriver::port_property(const Raul::Path& path, - const URI& uri, - const Atom& value) -{ -#ifdef HAVE_JACK_METADATA - EnginePort* eport = get_port(path); - if (eport) { - const jack_port_t* const jport = (const jack_port_t*)eport->handle(); - port_property_internal(jport, uri, value); - } -#endif -} - -void -JackDriver::port_property_internal(const jack_port_t* jport, - const URI& uri, - const Atom& value) -{ -#ifdef HAVE_JACK_METADATA - if (uri == _engine.world()->uris().lv2_name) { - jack_set_property(_client, jack_port_uuid(jport), - JACK_METADATA_PRETTY_NAME, value.ptr<char>(), "text/plain"); - } else if (uri == _engine.world()->uris().lv2_index) { - jack_set_property(_client, jack_port_uuid(jport), - JACKEY_ORDER, std::to_string(value.get<int32_t>()).c_str(), - "http://www.w3.org/2001/XMLSchema#integer"); - } else if (uri == _engine.world()->uris().rdf_type) { - if (value == _engine.world()->uris().lv2_CVPort) { - jack_set_property(_client, jack_port_uuid(jport), - JACKEY_SIGNAL_TYPE, "CV", "text/plain"); - } - } -#endif -} - -EnginePort* -JackDriver::create_port(DuplexPort* graph_port) -{ - EnginePort* eport = nullptr; - if (graph_port->is_a(PortType::AUDIO) || graph_port->is_a(PortType::CV)) { - // Audio buffer port, use Jack buffer directly - eport = new EnginePort(graph_port); - graph_port->set_is_driver_port(*_engine.buffer_factory()); - } else if (graph_port->is_a(PortType::ATOM) && - graph_port->buffer_type() == _engine.world()->uris().atom_Sequence) { - // Sequence port, make Jack port but use internal LV2 format buffer - eport = new EnginePort(graph_port); - } - - if (eport) { - register_port(*eport); - } - - return eport; -} - -void -JackDriver::pre_process_port(RunContext& context, EnginePort* port) -{ - const URIs& uris = context.engine().world()->uris(); - const SampleCount nframes = context.nframes(); - jack_port_t* jack_port = (jack_port_t*)port->handle(); - DuplexPort* graph_port = port->graph_port(); - Buffer* graph_buf = graph_port->buffer(0).get(); - void* jack_buf = jack_port_get_buffer(jack_port, nframes); - - if (graph_port->is_a(PortType::AUDIO) || graph_port->is_a(PortType::CV)) { - graph_port->set_driver_buffer(jack_buf, nframes * sizeof(float)); - if (graph_port->is_input()) { - graph_port->monitor(context); - } else { - graph_port->buffer(0)->clear(); // TODO: Avoid when possible - } - } else if (graph_port->buffer_type() == uris.atom_Sequence) { - graph_buf->prepare_write(context); - if (graph_port->is_input()) { - // Copy events from Jack port buffer into graph port buffer - const jack_nframes_t event_count = jack_midi_get_event_count(jack_buf); - for (jack_nframes_t i = 0; i < event_count; ++i) { - jack_midi_event_t ev; - jack_midi_event_get(&ev, jack_buf, i); - if (!graph_buf->append_event( - ev.time, ev.size, _midi_event_type, ev.buffer)) { - _engine.log().rt_error("Failed to write to MIDI buffer, events lost!\n"); - } - } - } - graph_port->monitor(context); - } -} - -void -JackDriver::post_process_port(RunContext& context, EnginePort* port) -{ - const URIs& uris = context.engine().world()->uris(); - const SampleCount nframes = context.nframes(); - jack_port_t* jack_port = (jack_port_t*)port->handle(); - DuplexPort* graph_port = port->graph_port(); - void* jack_buf = port->buffer(); - - if (port->graph_port()->is_output()) { - if (!jack_buf) { - // First cycle for a new output, so pre_process wasn't called - jack_buf = jack_port_get_buffer(jack_port, nframes); - port->set_buffer(jack_buf); - } - - if (graph_port->buffer_type() == uris.atom_Sequence) { - // Copy LV2 MIDI events to Jack MIDI buffer - Buffer* const graph_buf = graph_port->buffer(0).get(); - LV2_Atom_Sequence* seq = graph_buf->get<LV2_Atom_Sequence>(); - - jack_midi_clear_buffer(jack_buf); - LV2_ATOM_SEQUENCE_FOREACH(seq, ev) { - const uint8_t* buf = (const uint8_t*)LV2_ATOM_BODY(&ev->body); - if (ev->body.type == _midi_event_type) { - jack_midi_event_write( - jack_buf, ev->time.frames, buf, ev->body.size); - } - } - } - } - - // Reset graph port buffer pointer to no longer point to the Jack buffer - if (graph_port->is_driver_port()) { - graph_port->set_driver_buffer(nullptr, 0); - } -} - -void -JackDriver::append_time_events(RunContext& context, - Buffer& buffer) -{ - const URIs& uris = context.engine().world()->uris(); - const jack_position_t* pos = &_position; - const bool rolling = (_transport_state == JackTransportRolling); - - // Do nothing if there is no unexpected time change (other than rolling) - if (rolling == _old_rolling && - pos->frame == _old_frame && - pos->beats_per_minute == _old_bpm) { - return; - } - - // Update old time information to detect change next cycle - _old_frame = pos->frame; - _old_rolling = rolling; - _old_bpm = pos->beats_per_minute; - - // Build an LV2 position object to append to the buffer - LV2_Atom pos_buf[16]; - LV2_Atom_Forge_Frame frame; - lv2_atom_forge_set_buffer(&_forge, (uint8_t*)pos_buf, sizeof(pos_buf)); - lv2_atom_forge_object(&_forge, &frame, 0, uris.time_Position); - lv2_atom_forge_key(&_forge, uris.time_frame); - lv2_atom_forge_long(&_forge, pos->frame); - lv2_atom_forge_key(&_forge, uris.time_speed); - lv2_atom_forge_float(&_forge, rolling ? 1.0 : 0.0); - if (pos->valid & JackPositionBBT) { - lv2_atom_forge_key(&_forge, uris.time_barBeat); - lv2_atom_forge_float( - &_forge, pos->beat - 1 + (pos->tick / pos->ticks_per_beat)); - lv2_atom_forge_key(&_forge, uris.time_bar); - lv2_atom_forge_long(&_forge, pos->bar - 1); - lv2_atom_forge_key(&_forge, uris.time_beatUnit); - lv2_atom_forge_int(&_forge, pos->beat_type); - lv2_atom_forge_key(&_forge, uris.time_beatsPerBar); - lv2_atom_forge_float(&_forge, pos->beats_per_bar); - lv2_atom_forge_key(&_forge, uris.time_beatsPerMinute); - lv2_atom_forge_float(&_forge, pos->beats_per_minute); - } - - // Append position to buffer at offset 0 (start of this cycle) - LV2_Atom* lpos = (LV2_Atom*)pos_buf; - buffer.append_event( - 0, lpos->size, lpos->type, (const uint8_t*)LV2_ATOM_BODY_CONST(lpos)); -} - -/**** Jack Callbacks ****/ - -/** Jack process callback, drives entire audio thread. - * - * \callgraph - */ -REALTIME int -JackDriver::_process_cb(jack_nframes_t nframes) -{ - if (nframes == 0 || ! _is_activated) { - if (_flag) { - _sem.post(); - } - return 0; - } - - /* Note that Jack may not call this function for a cycle, if overloaded, - so a rolling counter here would not always be correct. */ - const jack_nframes_t start_of_current_cycle = jack_last_frame_time(_client); - - _transport_state = jack_transport_query(_client, &_position); - - _engine.locate(start_of_current_cycle, nframes); - - // Read input - for (auto& p : _ports) { - pre_process_port(_engine.run_context(), &p); - } - - // Process - _engine.run(nframes); - - // Write output - for (auto& p : _ports) { - post_process_port(_engine.run_context(), &p); - } - - // Update expected transport frame for next cycle to detect changes - if (_transport_state == JackTransportRolling) { - _old_frame += nframes; - } - - return 0; -} - -void -JackDriver::_thread_init_cb() -{ - ThreadManager::set_flag(THREAD_PROCESS); - ThreadManager::set_flag(THREAD_IS_REAL_TIME); -} - -void -JackDriver::_shutdown_cb() -{ - _engine.log().info("Jack shutdown, exiting\n"); - _is_activated = false; - _client = nullptr; -} - -int -JackDriver::_block_length_cb(jack_nframes_t nframes) -{ - if (_engine.root_graph()) { - _block_length = nframes; - _seq_size = jack_port_type_get_buffer_size(_client, JACK_DEFAULT_MIDI_TYPE); - _engine.root_graph()->set_buffer_size( - _engine.run_context(), *_engine.buffer_factory(), PortType::AUDIO, - _engine.buffer_factory()->audio_buffer_size(nframes)); - _engine.root_graph()->set_buffer_size( - _engine.run_context(), *_engine.buffer_factory(), PortType::ATOM, - _seq_size); - } - return 0; -} - -#ifdef INGEN_JACK_SESSION -void -JackDriver::_session_cb(jack_session_event_t* event) -{ - _engine.log().info(fmt("Jack session save to %1%\n") % event->session_dir); - - const std::string cmd = ( - boost::format("ingen -eg -n %1% -u %2% -l ${SESSION_DIR}") - % jack_get_client_name(_client) - % event->client_uuid).str(); - - SPtr<Serialiser> serialiser = _engine.world()->serialiser(); - if (serialiser) { - std::lock_guard<std::mutex> lock(_engine.world()->rdf_mutex()); - - SPtr<Node> root(_engine.root_graph(), NullDeleter<Node>); - serialiser->write_bundle(root, - URI(std::string("file://") + event->session_dir)); - } - - event->command_line = (char*)malloc(cmd.size() + 1); - memcpy(event->command_line, cmd.c_str(), cmd.size() + 1); - jack_session_reply(_client, event); - - switch (event->type) { - case JackSessionSave: - break; - case JackSessionSaveAndQuit: - _engine.log().warn("Jack session quit\n"); - _engine.quit(); - break; - case JackSessionSaveTemplate: - break; - } - - jack_session_event_free(event); -} -#endif - -} // namespace Server -} // namespace Ingen |