summaryrefslogtreecommitdiffstats
path: root/src/server/JackDriver.cpp
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2018-11-24 13:44:03 +0100
committerDavid Robillard <d@drobilla.net>2018-11-24 13:44:03 +0100
commita7d83f19b08eb4c6f79a82fe60c2b86db13f4420 (patch)
treed9b620bfba1e7462df4ddb3f6225cc5216c0ca81 /src/server/JackDriver.cpp
parentd63edc742cebd685f8a05936682210aa5c1e69a9 (diff)
downloadingen-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.cpp584
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