/*
This file is part of Ingen.
Copyright 2007-2017 David Robillard
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 .
*/
#include "ingen/Atom.hpp"
#include "ingen/Configuration.hpp"
#include "ingen/EngineBase.hpp"
#include "ingen/Interface.hpp"
#include "ingen/Message.hpp"
#include "ingen/Parser.hpp"
#include "ingen/URI.hpp"
#include "ingen/World.hpp"
#include "ingen/fmt.hpp"
#include "ingen/paths.hpp"
#include "ingen/runtime_paths.hpp"
#include "ingen_config.h"
#include "raul/Path.hpp"
#include "raul/Symbol.hpp"
#include "serd/serd.h"
#if USE_SOCKET
#include "ingen/client/SocketClient.hpp"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace ingen {
namespace {
class DummyInterface : public Interface
{
URI uri() const override { return URI("ingen:dummy"); }
void message(const Message& msg) override {}
};
std::unique_ptr world;
void
ingen_interrupt(int signal)
{
if (signal == SIGTERM) {
std::cerr << "ingen: Terminated\n";
exit(EXIT_FAILURE);
} else {
std::cout << "ingen: Interrupted\n";
if (world && world->engine()) {
world->engine()->quit();
}
}
}
void
ingen_try(bool cond, const char* msg)
{
if (!cond) {
std::cerr << "ingen: error: " << msg << "\n";
exit(EXIT_FAILURE);
}
}
int
print_version()
{
std::cout << "ingen " << INGEN_VERSION
<< " \n"
<< "Copyright 2007-2020 David Robillard .\n"
<< "License: \n"
<< "This is free software; you are free to change and redistribute it.\n"
<< "There is NO WARRANTY, to the extent permitted by law.\n";
return EXIT_SUCCESS;
}
int
run(int argc, char** argv)
{
// Create world
try {
world = std::make_unique(nullptr, nullptr, nullptr);
world->load_configuration(argc, argv);
if (argc <= 1) {
world->conf().print_usage("ingen", std::cout);
return EXIT_FAILURE;
}
if (world->conf().option("help").get()) {
world->conf().print_usage("ingen", std::cout);
return EXIT_SUCCESS;
}
if (world->conf().option("version").get()) {
return print_version();
}
} catch (std::exception& e) {
std::cout << "ingen: error: " << e.what() << "\n";
return EXIT_FAILURE;
}
Configuration& conf = world->conf();
if (conf.option("uuid").is_valid()) {
world->set_jack_uuid(conf.option("uuid").ptr());
}
// Run engine
if (conf.option("engine").get()) {
if (world->conf().option("threads").get() < 1) {
std::cerr << "ingen: error: threads must be > 0\n";
return EXIT_FAILURE;
}
ingen_try(world->load_module("server"), "Failed to load server module");
ingen_try(!!world->engine(), "Unable to create engine");
world->engine()->listen();
}
#if USE_SOCKET
client::SocketClient::register_factories(*world);
#endif
// Load GUI if requested
if (conf.option("gui").get()) {
ingen_try(world->load_module("client"), "Failed to load client module");
ingen_try(world->load_module("gui"), "Failed to load GUI module");
}
// If we don't have a local engine interface (from the GUI), use network
auto engine_interface = world->interface();
auto dummy_client = std::make_shared();
if (!engine_interface) {
const char* const uri = conf.option("connect").ptr();
ingen_try(URI::is_valid(uri),
fmt("Invalid URI <%1%>", uri).c_str());
engine_interface = world->new_interface(URI(uri), dummy_client);
if (!engine_interface && !conf.option("gui").get()) {
std::cerr << fmt("ingen: error: Failed to connect to `%1%'\n", uri);
return EXIT_FAILURE;
}
world->set_interface(engine_interface);
}
// Activate the engine, if we have one
if (world->engine()) {
if (!world->load_module("jack") && !world->load_module("portaudio")) {
std::cerr << "ingen: error: Failed to load driver module\n";
return EXIT_FAILURE;
}
if (!world->engine()->supports_dynamic_ports() &&
!conf.option("load").is_valid()) {
std::cerr << "ingen: error: Initial graph required for driver\n";
return EXIT_FAILURE;
}
}
// Load a graph
if (conf.option("load").is_valid()) {
std::optional parent;
std::optional symbol;
const Atom& path_option = conf.option("path");
if (path_option.is_valid()) {
if (raul::Path::is_valid(path_option.ptr())) {
const raul::Path p(path_option.ptr());
if (!p.is_root()) {
parent = p.parent();
symbol = raul::Symbol(p.symbol());
}
} else {
std::cerr << "Invalid path given: '" << path_option.ptr()
<< "\n";
}
}
ingen_try(!!world->parser(), "Failed to create parser");
const std::string graph = conf.option("load").ptr();
engine_interface->get(URI("ingen:/plugins"));
engine_interface->get(main_uri());
const std::lock_guard lock{world->rdf_mutex()};
world->parser()->parse_file(
*world, *engine_interface, graph, parent, symbol);
} else if (conf.option("server-load").is_valid()) {
const char* path = conf.option("server-load").ptr();
if (serd_uri_string_has_scheme(reinterpret_cast(path))) {
std::cout << "Loading " << path << " (server side)" << "\n";
engine_interface->copy(URI(path), main_uri());
} else {
SerdNode uri = serd_node_new_file_uri(
reinterpret_cast(path), nullptr, nullptr, true);
std::cout << "Loading " << reinterpret_cast(uri.buf)
<< " (server side)\n";
engine_interface->copy(URI(reinterpret_cast(uri.buf)),
main_uri());
serd_node_free(&uri);
}
}
// Save the currently loaded graph
if (conf.option("save").is_valid()) {
const char* path = conf.option("save").ptr();
if (serd_uri_string_has_scheme(reinterpret_cast(path))) {
std::cout << "Saving to " << path << "\n";
engine_interface->copy(main_uri(), URI(path));
} else {
SerdNode uri = serd_node_new_file_uri(
reinterpret_cast(path), nullptr, nullptr, true);
std::cout << "Saving to " << reinterpret_cast(uri.buf)
<< "\n";
engine_interface->copy(main_uri(), URI(reinterpret_cast(uri.buf)));
serd_node_free(&uri);
}
}
// Activate the engine now that the graph is loaded
if (world->engine()) {
world->engine()->flush_events(std::chrono::milliseconds(10));
world->engine()->activate();
}
// Set up signal handlers that will set quit_flag on interrupt
signal(SIGINT, ingen_interrupt);
signal(SIGTERM, ingen_interrupt);
if (conf.option("gui").get()) {
world->run_module("gui");
} else if (world->engine()) {
// Run engine main loop until interrupt
while (world->engine()->main_iteration()) {
std::this_thread::sleep_for(std::chrono::milliseconds(125));
}
}
// Sleep for a half second to allow event queues to drain
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// Shut down
if (world->engine()) {
world->engine()->deactivate();
}
// Save configuration to restore preferences on next run
const std::string path = conf.save(
world->uri_map(), "ingen", "options.ttl", Configuration::GLOBAL);
std::cout << fmt("Saved configuration to %1%\n", path);
engine_interface.reset();
return 0;
}
} // namespace
} // namespace ingen
int
main(int argc, char** argv)
{
ingen::set_bundle_path_from_code(
reinterpret_cast(&ingen::run));
return ingen::run(argc, argv);
}