Anklang-0.3.0.dev797+g4e3241f3 anklang-0.3.0.dev797+g4e3241f3
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
main.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "main.hh"
3#include "api.hh"
4#include "path.hh"
5#include "utils.hh"
6#include "jsonapi.hh"
7#include "driver.hh"
8#include "project.hh"
9#include "loft.hh"
10#include "compress.hh"
11#include "webui.hh"
12#include "server.hh"
13#include "internal.hh"
14#include "testing.hh"
15
16#include <limits.h>
17#include <stdlib.h>
18#include <unistd.h>
19#include <signal.h>
20#include <malloc.h>
21#include <unistd.h>
22#include <fcntl.h>
23#ifdef ASE_WITH_CPPTRACE
24#include <cpptrace/from_current.hpp>
25#endif
26
27#include "trkn.hh"
28
29#undef B0 // undo pollution from termios.h
30
31#define MDEBUG(...) Ase::debug ("memory", __VA_ARGS__)
32
33namespace Ase {
34
36 MainAppImpl ();
37};
38MainAppImpl main_app;
39const MainApp &App = main_app;
40
41MainAppImpl::MainAppImpl()
42{}
43
44LoopP main_loop = Loop::current();
45static String arg_ui_mode;
46static int arg_unauth_port = 0;
47
48// == JobQueue ==
49static void
50call_main_loop (const std::function<void()> &fun)
51{
52 main_loop->add (fun);
53}
54JobQueue main_jobs (call_main_loop);
55
56// == MainConfig and arguments ==
57static void
58print_usage (bool help)
59{
60 if (!help)
61 {
62 printout ("%s %s\n", executable_name(), ase_version());
63 printout ("Build: %s\n", ase_build_id());
64 return;
65 }
66 printout ("Usage: %s [OPTIONS] [project.anklang]\n", executable_name());
67 printout (" --check Run integrity tests\n");
68 printout (" --disable-randomization Test mode for deterministic tests\n");
69 printout (" --fatal-warnings Abort on warnings and failing assertions\n");
70 printout (" --help Print program usage and options\n");
71 printout (" --jsbin Print Javascript IPC & binary messages\n");
72 printout (" --jsipc Print Javascript IPC messages\n");
73 printout (" --jsonts Print TypeScript bindings\n");
74 printout (" --list-drivers Print PCM and MIDI drivers\n");
75 printout (" --list-tests List all test names\n");
76 printout (" --norc Prevent loading of any rc files\n");
77 printout (" --play-autostart Automatically start playback of `project.anklang`\n");
78 printout (" --rand64 Produce 64bit random numbers on stdout\n");
79 printout (" --test[=test] Run specific tests\n");
80 printout (" --unauth-dev=NUM Open an unauthenticated websocket port for testing\n");
81 printout (" --ui <none|chromium|google-chrome|htmlgui>\n");
82 printout (" Open GUI in web browser [htmlgui]\n");
83 printout (" --version Print program version\n");
84 printout (" -M mididriver Force use of <mididriver>\n");
85 printout (" -P pcmdriver Force use of <pcmdriver>\n");
86 printout (" -o wavfile Capture output to OPUS/FLAC/WAV file\n");
87 printout (" -t <time> Automatically play and stop after <time> has passed\n"); // -t <time>[{,|;}tailtime]
88 printout ("Options set via $ASE_DEBUG:\n");
89 printout (" :no-logfile: Disable logging to ~/.cache/anklang/ instead of stderr\n");
90}
91
93static bool
94parse_option_arg (const char *option, char **argv, unsigned *ith, const char **argp)
95{
96 const size_t l = strlen (option);
97 if (strncmp (option, argv[*ith], l) == 0) {
98 *argp = argv[*ith] + l;
99 argv[*ith] = nullptr;
100 if ((*argp)[0] == '=')
101 *argp += 1;
102 else if ((*argp)[0] == 0) {
103 *ith += 1;
104 *argp = argv[*ith] ? argv[*ith] : "";
105 argv[*ith] = nullptr;
106 }
107 return true;
108 }
109 return false;
110}
111
112// 1:ERROR 2:FAILED+REJECT 4:IO 8:MESSAGE 16:GET 256:BINARY
113static constexpr int jsipc_logflags = 1 | 2 | 4 | 8 | 16;
114static constexpr int jsbin_logflags = 1 | 256;
115
116static StringS check_test_names;
117
118static void
119parse_args (int *argcp, char **argv, MainAppImpl &config)
120{
121 if (0) // allow jsipc logging via ASE_DEBUG ?
122 {
123 config.jsonapi_logflags |= debug_key_enabled ("jsbin") ? jsbin_logflags : 0;
124 config.jsonapi_logflags |= debug_key_enabled ("jsipc") ? jsipc_logflags : 0;
125 }
126
127 config.norc = false;
128 bool sep = false; // -- separator
129 std::string default_ui_mode = "htmlgui";
130 const uint argc = *argcp;
131 for (uint i = 1; i < argc; i++)
132 {
133 const char *optarg = nullptr;
134 if (sep)
135 config.args.push_back (argv[i]);
136 else if (strcmp (argv[i], "--fatal-warnings") == 0 || strcmp (argv[i], "--g-fatal-warnings") == 0)
138 else if (strcmp ("--disable-randomization", argv[i]) == 0)
139 config.allow_randomization = false;
140 else if (strcmp ("--norc", argv[i]) == 0)
141 config.norc = true;
142 else if (strcmp ("--rand64", argv[i]) == 0)
143 {
144 FastRng prng;
145 constexpr int N = 8192;
146 uint64_t buffer[N];
147 while (1)
148 {
149 for (size_t i = 0; i < N; i++)
150 buffer[i] = prng.next();
151 fwrite (buffer, sizeof (buffer[0]), N, stdout);
152 }
153 exit (0);
154 }
155 else if (strcmp ("--check", argv[i]) == 0)
156 {
157 config.mode = MainApp::CHECK_INTEGRITY_TESTS;
159 printerr ("CHECK_INTEGRITY_TESTS…\n");
160 default_ui_mode = "none";
161 }
162 else if (strcmp ("--list-tests", argv[i]) == 0)
163 {
165 for (const auto &t : Test::list_tests())
166 ids.push_back (t.ident);
167 std::sort (ids.begin(), ids.end());
168 for (const auto &t : ids)
169 printout ("%s\n", t);
170 exit (0);
171 }
172 else if (strcmp ("--test", argv[i]) == 0 || strncmp ("--test=", argv[i], 7) == 0)
173 {
174 const char *eq = strchr (argv[i], '=');
175 const char *arg = eq ? eq + 1 : i+1 < argc ? argv[++i] : nullptr;
176 config.mode = MainApp::CHECK_INTEGRITY_TESTS;
178 if (arg)
179 check_test_names.push_back (arg);
180 default_ui_mode = "none";
181 }
182 else if (argv[i] == String ("--blake3") && i + 1 < size_t (argc))
183 {
184 argv[i++] = nullptr;
185 String hash = blake3_hash_file (argv[i]);
186 if (hash.empty())
187 printerr ("%s: failed to read: %s\n", argv[i], strerror (errno));
188 else
189 printout ("%s\n", string_to_hex (hash));
190 exit (hash == "");
191 }
192 else if (strcmp ("--jsonts", argv[i]) == 0) {
193 if (getenv ("ASE_JSONTS") == nullptr)
194 fatal_error ("%s: environment must contain ASE_JSONTS for --jsonts", argv[0]);
195 printout ("%s\n", Jsonipc::g_binding_printer->finish());
196 exit (0);
197 } else if (strcmp ("--jsipc", argv[i]) == 0)
198 config.jsonapi_logflags |= jsipc_logflags;
199 else if (strcmp ("--jsbin", argv[i]) == 0)
200 config.jsonapi_logflags |= jsbin_logflags;
201 else if (strcmp ("--list-drivers", argv[i]) == 0)
202 config.list_drivers = true;
203 else if (strcmp ("-M", argv[i]) == 0 && i + 1 < size_t (argc))
204 {
205 argv[i++] = nullptr;
206 config.midi_override = argv[i];
207 }
208 else if (strcmp ("-P", argv[i]) == 0 && i + 1 < size_t (argc))
209 {
210 argv[i++] = nullptr;
211 config.pcm_override = argv[i];
212 }
213 else if (strcmp ("--no-devices", argv[i]) == 0)
214 {
215 config.no_devices = true;
216 }
217 else if (strcmp ("-h", argv[i]) == 0 ||
218 strcmp ("--help", argv[i]) == 0)
219 {
220 print_usage (true);
221 exit (0);
222 }
223 else if (strcmp ("--version", argv[i]) == 0)
224 {
225 print_usage (false);
226 exit (0);
227 }
228 else if (argv[i] == String ("-o") && i + 1 < size_t (argc))
229 {
230 argv[i++] = nullptr;
231 config.outputfile = argv[i];
232 }
233 else if (argv[i] == String ("--play-autostart"))
234 {
235 config.play_autostart = true;
236 default_ui_mode = "none";
237 }
238 else if (parse_option_arg ("--unauth-dev", argv, &i, &optarg))
239 {
240 arg_unauth_port = string_to_int (optarg);
241 default_ui_mode = "wait";
242 }
243 else if (argv[i] == String ("-t") && i + 1 < size_t (argc))
244 {
245 config.play_autostart = true;
246 argv[i++] = nullptr;
247 config.play_autostop = string_to_seconds (argv[i]);
248 default_ui_mode = "none";
249 }
250 else if (parse_option_arg ("--ui", argv, &i, &optarg))
251 {
252 arg_ui_mode = optarg;
253 }
254 else if (argv[i] == String ("--") && !sep)
255 sep = true;
256 else if (argv[i][0] == '-' && !sep)
257 fatal_error ("invalid command line argument: %s", argv[i]);
258 else
259 config.args.push_back (argv[i]);
260 argv[i] = nullptr;
261 }
262 if (arg_ui_mode.empty())
263 arg_ui_mode = default_ui_mode;
264 if (*argcp > 1)
265 {
266 uint e = 1;
267 for (uint i = 1; i < argc; i++)
268 if (argv[i])
269 {
270 argv[e++] = argv[i];
271 if (i >= e)
272 argv[i] = nullptr;
273 }
274 *argcp = e;
275 }
276}
277
278static String
279make_auth_string()
280{
281 const char *const c52 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz";
282 /* We use WebScoket subprotocol randomization as authentication, so:
283 * a) Authentication happens *before* message interpretation, so an
284 * unauthenticated sender cannot cause crahses via e.g. rapidjson exceptions.
285 * b) To serve as working authentication measure, the subprotocol random string
286 * must be cryptographically-secure.
287 */
288 KeccakCryptoRng csprng;
289 String auth = "sessC";
290 for (size_t i = 0; i < 23; ++i)
291 auth += c52[csprng.random() % 52]; // each step adds 5.7 bits
292 return auth;
293}
294
295static void
296run_tests_and_quit ()
297{
298 if (check_test_names.empty())
299 Test::run();
300 else
301 Test::run (check_test_names);
302 main_loop->quit (0);
303}
304
305void
307{
308 LoopP loop = main_loop;
309 if (loop)
310 loop->wakeup();
311}
312
313static std::atomic<bool> seen_autostop = false;
314
315// Lock and obstruction-free autostop trigger.
316void
318{
319 if (!seen_autostop)
320 {
321 seen_autostop = true;
323 }
324}
325
326static bool
327handle_autostop (const LoopState &state)
328{
329 switch (state.phase)
330 {
331 case LoopState::PREPARE: return seen_autostop;
332 case LoopState::CHECK: return seen_autostop;
333 case LoopState::DISPATCH:
334 info ("Main: stopping playback (auto)");
335 main_loop->quit (0);
336 return true; // keep alive
337 default: ;
338 }
339 return false;
340}
341
342static void
343init_sigpipe()
344{
345 // don't die if we write() data to a process and that process dies (i.e. jackd)
346 sigset_t signal_mask;
347 sigemptyset (&signal_mask);
348 sigaddset (&signal_mask, SIGPIPE);
349
350 int rc = pthread_sigmask (SIG_BLOCK, &signal_mask, NULL);
351 if (rc != 0)
352 Ase::warning ("Ase: pthread_sigmask for SIGPIPE failed: %s\n", strerror (errno));
353}
354
355static std::atomic<bool> loft_needs_preallocation_mt = false;
356
357// handle watermark underrun notifications
358static void
359notify_loft_lowmem ()
360{
361 if (!loft_needs_preallocation_mt)
362 {
363 loft_needs_preallocation_mt = true;
365 }
366}
367
368static size_t last_loft_preallocation = 0;
369
370static void
371preallocate_loft (size_t preallocation)
372{
373 using namespace Ase;
374 last_loft_preallocation = preallocation;
375 LoftConfig loftcfg = {
376 .preallocate = last_loft_preallocation,
377 .watermark = last_loft_preallocation / 2,
378 .flags = Loft::PREFAULT_PAGES,
379 };
380 loft_set_config (loftcfg);
381 loft_set_notifier (notify_loft_lowmem);
382 loft_grow_preallocate();
383}
384
385static bool
386dispatch_loft_lowmem (const Ase::LoopState &lstate)
387{
388 using namespace Ase;
389 const bool keep_alive = lstate.phase == LoopState::DISPATCH;
390 // generally, dispatch logic may only run in LoopState::DISPATCH, but this handler
391 // makes a rare exception, because we try to get ahead of concurrently runnint RT-threads...
392 return_unless (loft_needs_preallocation_mt, keep_alive);
393 loft_needs_preallocation_mt = false;
394 last_loft_preallocation *= 2;
395 const size_t newalloc = loft_grow_preallocate (last_loft_preallocation);
396 LoftConfig config;
397 loft_get_config (config);
398 config.watermark = last_loft_preallocation / 2;
399 loft_set_config (config);
400 if (newalloc > 0)
401 MDEBUG ("Loft preallocation in main thread: %f MB", newalloc / (1024. * 1024));
402 return keep_alive;
403}
404
405static void
406prefault_pages (size_t stacksize, size_t heapsize)
407{
408 const size_t pagesize = sysconf (_SC_PAGESIZE);
409 char *heap = (char*) malloc (heapsize);
410 if (heap)
411 for (size_t i = 0; i < heapsize; i += pagesize)
412 heap[i] = 1;
413 free (heap);
414 char *stack = (char*) alloca (stacksize);
415 if (stack)
416 for (size_t i = 0; i < stacksize; i += pagesize)
417 stack[i] = 1;
418}
419
420static int
421main (int argc, char *argv[])
422{
423 using namespace Ase;
424 using namespace AnsiColors;
425
426 // setup thread identifier
427 TaskRegistry::setup_ase ("AnklangMainProc");
428 // use malloc to serve allocations via sbrk only (avoid mmap)
429 mallopt (M_MMAP_MAX, 0);
430 // avoid releasing sbrk memory back to the system (reduce page faults)
431 mallopt (M_TRIM_THRESHOLD, -1);
432 // reserve large sbrk area and reduce page faults for heap and stack
433 prefault_pages ((1024 + 768) * 1024, 64 * 1024 * 1024);
434 // preallocate memory for lock-free allocator
435 preallocate_loft (64 * 1024 * 1024);
436 // warn if preallocation is not sufficient
437 loft_set_growth_notifier ([] (size_t total, size_t needed)
438 {
439 warning ("Loft.BumpAllocator: growing beyond preallocation: totalmem=%u needed=%d\n", total, needed);
440 });
441
442 // print stack trace for uncaught exceptions
443 logging_handle_terminate();
444
445 // SIGPIPE init: needs to be done before any child thread is created
446 init_sigpipe();
447 if (setpgid (0, 0) < 0)
448 diag ("Main: setpgid failed: %s", ::strerror (errno));
449
450 // apply user locale
451 if (!setlocale (LC_ALL, ""))
452 fatal_error ("setlocale: locale not supported by libc: %s", ::strerror (errno));
453
454 // parse args and config
455 parse_args (&argc, argv, main_app);
456 logging_configure (arg_ui_mode != "none");
457
458 // handle loft preallocation needs
459 main_loop->exec_dispatcher (dispatch_loft_lowmem, LoopPriority::SYSALLOC);
460
461 // load preferences unless --norc was given
462 if (!App.norc)
463 Preference::load_preferences (true);
464
465 // Ensure Ase server exists
466 ServerImpl::instancep();
467
468 // tracktion initialisation
469 if (!trkn_init (argc, argv, App.no_devices))
470 fatal_error ("Main: failed to initialize tracktion engine");
471
472 const auto B1 = color (BOLD);
473 const auto B0 = color (BOLD_OFF);
474
475 // load drivers and dump device list
477 if (App.list_drivers)
478 {
479 Ase::Driver::EntryVec entries;
480 printout ("%s", _("Available PCM drivers:\n"));
481 entries = Ase::PcmDriver::list_drivers();
482 std::sort (entries.begin(), entries.end(), [] (auto &a, auto &b) { return a.priority < b.priority; });
483 for (const auto &entry : entries)
484 {
485 printout (" %-30s (%s, %08x)\n\t%s\n%s%s%s%s", entry.devid + ":",
486 entry.readonly ? "Input" : entry.writeonly ? "Output" : "Duplex",
487 entry.priority, entry.device_name,
488 entry.capabilities.empty() ? "" : "\t" + entry.capabilities + "\n",
489 entry.device_info.empty() ? "" : "\t" + entry.device_info + "\n",
490 entry.hints.empty() ? "" : "\t(" + entry.hints + ")\n",
491 entry.notice.empty() ? "" : "\t" + entry.notice + "\n");
492 if (debug_key_enabled ("driver"))
493 printerr (" %08x: %s\n", entry.priority, Driver::priority_string (entry.priority));
494 }
495 printout ("%s", _("Available MIDI drivers:\n"));
496 entries = Ase::MidiDriver::list_drivers();
497 std::sort (entries.begin(), entries.end(), [] (auto &a, auto &b) { return a.priority < b.priority; });
498 for (const auto &entry : entries)
499 {
500 printout (" %-30s (%s, %08x)\n\t%s\n%s%s%s%s", entry.devid + ":",
501 entry.readonly ? "Input" : entry.writeonly ? "Output" : "Duplex",
502 entry.priority, entry.device_name,
503 entry.capabilities.empty() ? "" : "\t" + entry.capabilities + "\n",
504 entry.device_info.empty() ? "" : "\t" + entry.device_info + "\n",
505 entry.hints.empty() ? "" : "\t(" + entry.hints + ")\n",
506 entry.notice.empty() ? "" : "\t" + entry.notice + "\n");
507 if (debug_key_enabled ("driver"))
508 printerr (" %08x: %s\n", entry.priority, Driver::priority_string (entry.priority));
509 }
510 return 0;
511 }
512
513 // load projects
514 ProjectImplP preload_project;
515 for (const auto &filename : App.args)
516 {
517 preload_project = ProjectImpl::create (Path::basename (filename));
518 Error error = Error::NO_MEMORY;
519 if (preload_project)
520 error = preload_project->load_project (filename);
521 diag ("Main: load project: %s: %s", filename, ase_error_blurb (error));
522 if (!!error)
523 warning ("%s: failed to load project: %s", filename, ase_error_blurb (error));
524 }
525
526 // open Jsonapi socket
527 const String auth_token = arg_unauth_port > 0 ? "" : make_auth_string();
528 auto wss = WebSocketServer::create (jsonapi_make_connection, App.jsonapi_logflags, auth_token);
529 main_app.web_socket_server = &*wss;
530 wss->http_dir (anklang_runpath (RPath::INSTALLDIR, "/ui/"));
531 // wss->http_alias ("/User/Controller", anklang_home_dir ("/Controller"));
532 wss->http_alias ("/Builtin/Controller", anklang_runpath (RPath::INSTALLDIR, "/Controller"));
533 // wss->http_alias ("/User/Scripts", anklang_home_dir ("/Scripts"));
534 wss->http_alias ("/Builtin/Scripts", anklang_runpath (RPath::INSTALLDIR,"/Scripts"));
535 const int xport = arg_unauth_port > 0 ? arg_unauth_port : 0;
536 const String subprotocol = ""; // make_auth_string()
537 jsonapi_set_subprotocol (subprotocol);
538 if (App.mode == MainApp::SYNTHENGINE && arg_ui_mode != "none") {
539 const char *host = "127.0.0.1";
540 wss->listen (host, xport, [] () { main_loop->quit (-1); });
541 std::string webui_url = wss->url();
542 if (!xport) {
543 String redirecthtml = webui_create_auth_redirect ("anklang", wss->listen_port(), auth_token, arg_ui_mode);
544 if (errno)
545 fatal_error ("%s: failed to create html redirect file in $HOME", redirecthtml);
546 webui_url = "file://" + redirecthtml;
547 wss->see_other (webui_url);
548 }
549 info ("Main: WebUI address: %s", webui_url);
550 auto ereason = webui_start_browser (arg_ui_mode, main_loop, webui_url, [] () { main_loop->quit (0); });
551 if (ereason.error)
552 fatal_error ("Main: failed to run WebUI: %s: %s", ereason.what, ::strerror (ereason.error));
553 }
554
555 // run atquit handler on SIGHUP SIGINT
556 for (int sigid : { SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGTERM, SIGSYS }) {
557 main_loop->exec_usignal (sigid, [] (int8 sig) {
558 info ("Main: got signal %d: terminate", sig);
559 const pid_t pgid = getpgrp();
560 atquit_terminate (-1, pgid);
561 return false;
562 });
563 USignalSource::install_sigaction (sigid);
564 }
565
566 // catch SIGUSR2 to close sockets
567 main_loop->exec_usignal (SIGUSR2, [wss] (int8 sig) {
568 info ("Main: got signal %d: reset WebSocket", sig);
569 wss->reset();
570 return true;
571 });
572 USignalSource::install_sigaction (SIGUSR2);
573
574 // start output capturing
575 if (App.outputfile)
576 ; // TODO: implement capturing
577
578 // start auto play
579 if (App.play_autostart && preload_project)
580 main_loop->add ([preload_project] ()
581 {
582 info ("Main: starting playback (auto)");
583 preload_project->start_playback (App.play_autostop);
585 // handle automatic shutdown
586 main_loop->exec_dispatcher (handle_autostop);
587
588 // run test suite
589 if (App.mode == MainApp::CHECK_INTEGRITY_TESTS)
590 main_loop->add (run_tests_and_quit);
591
592 // run main event loop and catch SIGUSR2
593 const int exitcode = main_loop->run();
594 assert_return (main_loop, -1); // ptr must be kept around
595 diag ("Main: event loop quit: code=%d", exitcode);
596
597 // cleanup
598 wss->shutdown(); // close socket, allow no more calls
599 main_app.web_socket_server = nullptr;
600 wss = nullptr;
601
602 // deactivate any projects, releases Audio resources
603 ProjectImpl::force_shutdown_all();
604
605 // halt audio engine, join its threads, dispatch cleanups
606 main_loop->iterate_pending();
607
608 // shutdown tracktion *after* main loop stopped
609 trkn_shutdown ();
610
611 diag ("Main: exiting: %d", exitcode);
612 return exitcode;
613}
614
615} // Ase
616
617int
618main (int argc, char *argv[])
619{
620 int r = -128;
621#ifdef ASE_WITH_CPPTRACE
622 CPPTRACE_TRY { r = Ase::main (argc, argv); }
623 CPPTRACE_CATCH (const std::exception& e) {
624 std::string msg = "Exception: ";
625 msg += e.what();
626 msg += "\n";
627 fflush (stdout);
628 fputs (msg.c_str(), stderr);
629 fflush (stderr);
630 cpptrace::from_current_exception().print();
631 }
632#else
633 r = Ase::main (argc, argv);
634#endif
635 return r;
636}
T c_str(T... args)
static String priority_string(uint priority)
Return string which represents the given priority mask.
Definition driver.cc:31
static LoopP current()
Return the thread-local singleton loop, created on first call.
Definition loop.cc:306
T empty(T... args)
exit
fflush
fputs
free
fwrite
optarg
getpgrp
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:73
#define _(...)
Retrieve the translation of a C or C++ string.
Definition internal.hh:18
std::string color(Colors acolor, Colors c1, Colors c2, Colors c3, Colors c4, Colors c5, Colors c6)
Return ANSI code for the specified color if stdout & stderr should be colorized, see colorize_tty().
Definition platform.cc:191
String basename(const String &path)
Strips all directory components from path and returns the resulting file name.
Definition path.cc:68
int run(void)
Run all registered tests.
Definition testing.cc:264
The Anklang C++ API namespace.
Definition api.hh:9
bool trkn_init(int argc, char *argv[], bool nodevs)
Setup tracktion and tracktion::engine.
Definition trkn.cc:99
String string_to_hex(const String &input)
Convert bytes in string input to hexadecimal numbers.
Definition strings.cc:1171
int8_t int8
An 8-bit signed integer.
Definition cxxaux.hh:26
std::vector< String > StringS
Convenience alias for a std::vector<std::string>.
Definition cxxaux.hh:36
JobQueue main_jobs(call_main_loop)
Execute a job callback in the event loop.
Definition main.hh:42
Error
Enum representing Error states.
Definition api.hh:22
const char * ase_error_blurb(Error error)
Describe Error condition.
Definition server.cc:220
std::string anklang_runpath(RPath rpath, const String &segment)
Retrieve various resource paths at runtime.
Definition platform.cc:58
double string_to_seconds(const String &string, double fallback)
Parse string into seconds.
Definition strings.cc:773
int64 string_to_int(const String &string, size_t *consumed, uint base)
Parse a string into a 64bit integer, optionally specifying the expected number base.
Definition strings.cc:578
void main_loop_wakeup()
Wake up the event loop.
Definition main.cc:306
bool logging_fatal_warnings
Global flag to cause the program to abort on warnings.
Definition logging.cc:281
@ IDLE
Mildly important, used for background tasks.
@ SYSALLOC
Internal maintenance, don't use.
const char * ase_version()
Provide a string containing the package version.
Definition platform.cc:803
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
std::string executable_name()
Retrieve the name part of executable_path().
Definition platform.cc:742
void main_loop_autostop_mt()
Stop the event loop after a timeout.
Definition main.cc:317
bool debug_key_enabled(const char *conditional) noexcept
Check if conditional is enabled by $ASE_DEBUG.
Definition logging.cc:297
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
void load_registered_drivers()
Load all registered drivers.
Definition driver.cc:76
size_t preallocate
Amount of preallocated available memory.
Definition loft.hh:45
size_t watermark
Watermark to trigger async preallocation.
Definition loft.hh:46
const char * ase_build_id()
Provide a string containing the ASE library build id.
Definition platform.cc:809
Configuration for Loft allocations.
Definition loft.hh:44
size_t hash(size_t seed, const T &v)
pthread_sigmask
T push_back(T... args)
sigaddset
sigemptyset
T sort(T... args)
typedef uint64_t
strchr
strlen
typedef pid_t
sysconf
T what(T... args)