/* 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 "TestClient.hpp" #include "ingen/Atom.hpp" #include "ingen/AtomForge.hpp" #include "ingen/AtomReader.hpp" #include "ingen/Configuration.hpp" #include "ingen/EngineBase.hpp" #include "ingen/FilePath.hpp" #include "ingen/Interface.hpp" #include "ingen/Parser.hpp" #include "ingen/Serialiser.hpp" #include "ingen/Store.hpp" #include "ingen/URI.hpp" #include "ingen/URIMap.hpp" #include "ingen/World.hpp" #include "ingen/fmt.hpp" #include "ingen/memory.hpp" #include "ingen/runtime_paths.hpp" #include "raul/Path.hpp" #include "serd/serd.h" #include "sord/sordmm.hpp" #include "sratom/sratom.h" #include #include #include #include #include #include #include #include #include #include namespace ingen::test { namespace { std::unique_ptr world; void ingen_try(bool cond, const char* msg) { if (!cond) { std::cerr << "ingen: Error: " << msg << "\n"; world.reset(); exit(EXIT_FAILURE); } } FilePath real_file_path(const char* path) { const std::unique_ptr> real_path{realpath(path, nullptr), FreeDeleter{}}; return FilePath{real_path.get()}; } int run(int argc, char** argv) { // Create world try { world = std::make_unique(nullptr, nullptr, nullptr); world->load_configuration(argc, argv); } catch (const std::exception& e) { std::cerr << "ingen: " << e.what() << "\n"; return EXIT_FAILURE; } // Get mandatory command line arguments const Atom& load = world->conf().option("load"); const Atom& execute = world->conf().option("execute"); if (!load.is_valid() || !execute.is_valid()) { std::cerr << "Usage: ingen_test --load START_GRAPH --execute COMMANDS_FILE\n"; return EXIT_FAILURE; } // Get start graph and commands file options const FilePath load_path = real_file_path(static_cast(load.get_body())); const FilePath run_path = real_file_path(static_cast(execute.get_body())); if (load_path.empty()) { std::cerr << "error: initial graph '" << load_path << "' does not exist\n"; return EXIT_FAILURE; } if (run_path.empty()) { std::cerr << "error: command file '" << run_path << "' does not exist\n"; return EXIT_FAILURE; } // Load modules ingen_try(world->load_module("server"), "Unable to load server module"); // Initialise engine ingen_try(bool(world->engine()), "Unable to create engine"); world->engine()->init(48000.0, 4096, 4096); world->engine()->activate(); // Load graph if (!world->parser()->parse_file(*world, *world->interface(), load_path)) { std::cerr << "error: failed to load initial graph " << load_path << "\n"; return EXIT_FAILURE; } world->engine()->flush_events(std::chrono::milliseconds(20)); // Read commands AtomForge forge(world->uri_map().urid_map()); sratom_set_object_mode(&forge.sratom(), SRATOM_OBJECT_MODE_BLANK_SUBJECT); // AtomReader to read commands from a file and send them to engine AtomReader atom_reader(world->uri_map(), world->uris(), world->log(), *world->interface()); // AtomWriter to serialise responses from the engine const std::shared_ptr client{new TestClient(world->log())}; world->interface()->set_respondee(client); world->engine()->register_client(client); SerdURI cmds_base; SerdNode cmds_file_uri = serd_node_new_file_uri( reinterpret_cast(run_path.c_str()), nullptr, &cmds_base, true); auto* cmds = new Sord::Model(*world->rdf_world(), reinterpret_cast(cmds_file_uri.buf)); SerdEnv* env = serd_env_new(&cmds_file_uri); cmds->load_file(env, SERD_TURTLE, run_path); const Sord::Node nil; int n_events = 0; for (;; ++n_events) { const std::string subject_str = fmt("msg%1%", n_events); Sord::URI subject(*world->rdf_world(), subject_str, reinterpret_cast(cmds_file_uri.buf)); auto iter = cmds->find(subject, nil, nil); if (iter.end()) { break; } forge.clear(); forge.read(*world->rdf_world(), cmds->c_obj(), subject.c_obj()); #if 0 const LV2_Atom* atom = forge.atom(); cerr << "READ " << atom->size << " BYTES\n"; cerr << sratom_to_turtle( sratom, &world->uri_map().urid_unmap_feature()->urid_unmap, (const char*)cmds_file_uri.buf, nullptr, nullptr, atom->type, atom->size, LV2_ATOM_BODY(atom)) << endl; #endif if (!atom_reader.write(forge.atom(), n_events + 1)) { return EXIT_FAILURE; } world->engine()->flush_events(std::chrono::milliseconds(20)); } delete cmds; // Save resulting graph auto r = world->store()->find(raul::Path("/")); const std::string base = run_path.stem(); const std::string out_name = base.substr(0, base.find('.')) + ".out.ingen"; const FilePath out_path = std::filesystem::current_path() / out_name; world->serialiser()->write_bundle(r->second, URI(out_path)); // Undo every event (makes the graph identical to the original) for (int i = 0; i < n_events; ++i) { world->interface()->undo(); world->engine()->flush_events(std::chrono::milliseconds(20)); } // Save completely undone graph r = world->store()->find(raul::Path("/")); const std::string undo_name = base.substr(0, base.find('.')) + ".undo.ingen"; const FilePath undo_path = std::filesystem::current_path() / undo_name; world->serialiser()->write_bundle(r->second, URI(undo_path)); // Redo every event (makes the graph identical to the pre-undo output) for (int i = 0; i < n_events; ++i) { world->interface()->redo(); world->engine()->flush_events(std::chrono::milliseconds(20)); } // Save completely redone graph r = world->store()->find(raul::Path("/")); const std::string redo_name = base.substr(0, base.find('.')) + ".redo.ingen"; const FilePath redo_path = std::filesystem::current_path() / redo_name; world->serialiser()->write_bundle(r->second, URI(redo_path)); serd_env_free(env); serd_node_free(&cmds_file_uri); // Shut down world->engine()->deactivate(); return EXIT_SUCCESS; } } // namespace } // namespace ingen::test int main(int argc, char** argv) { try { ingen::set_bundle_path_from_code( reinterpret_cast(&ingen::test::ingen_try)); return ingen::test::run(argc, argv); } catch (const std::exception& e) { std::cerr << "ingen: " << e.what() << "\n"; return EXIT_FAILURE; } }