21static constexpr size_t telemetry_size = 4 * 1024 * 1024;
23ServerImpl *SERVER =
nullptr;
25ServerImpl::ServerImpl () :
26 telemetry_arena (telemetry_size)
29 Block telemetry_header = telemetry_arena.allocate (64);
31 const uint8_t header_sentinel[64] = {
32 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03,
33 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07,
34 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b,
35 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f,
37 assert_return (telemetry_header.block_length == sizeof (header_sentinel));
38 memcpy (telemetry_header.block_start, header_sentinel, telemetry_header.block_length);
43ServerImpl::~ServerImpl ()
45 fatal_error (
"ServerImpl references must persist");
51ServerImpl::get_version ()
57ServerImpl::get_build_id ()
63ServerImpl::get_opus_version ()
65 return wave_writer_opus_version();
69ServerImpl::get_flac_version ()
71 return wave_writer_flac_version();
75ServerImpl::get_sndfile_version ()
77 return libsndfile_version();
81ServerImpl::shutdown ()
88ServerImpl::last_project ()
90 return Project::last_project();
94ServerImpl::create_project (
String projectname)
96 return ProjectImpl::create (projectname);
100ServerImpl::list_preferences ()
102 const CStringS list = Preference::list();
107ServerImpl::access_preference (
const String &ident)
109 return Preference::find (ident);
113ServerImpl::ui_config ()
125 bool has_failed =
false;
129ServerImpl::ui_test_fetch ()
132 if (ui_test_state.tests.empty() && !App.ui_tests.
empty()) {
133 ui_test_state.tests = App.ui_tests;
134 ui_test_state.current = -1;
135 ui_test_state.has_failed =
false;
138 if (ui_test_state.has_failed || ui_test_state.current + 1 >= ui_test_state.tests.size())
141 const String testname = ui_test_state.tests[++ui_test_state.current];
142 printerr (
"%s %s\n",
" CHECKING ", testname);
147ServerImpl::ui_test_report (
const String &testname,
bool success)
149 if (success && ui_test_state.current >= 0 &&
150 ui_test_state.current < ui_test_state.tests.size() &&
151 testname == ui_test_state.tests[ui_test_state.current])
152 printerr (
"%s %s\n",
" PASS ", testname);
154 printerr (
"%s %s\n",
" FAIL ", testname);
155 ui_test_state.has_failed =
true;
158 if (ui_test_state.current >= ui_test_state.tests.size() || ui_test_state.has_failed)
159 main_loop->add ([] () { main_loop->quit (ui_test_state.has_failed ? 1 : 0); });
163ServerImpl::instancep ()
172 const String ckey = canonify_key (key);
173 if (ckey.
size() && ckey[0] !=
'_')
175 const String filename = Path::join (Path::xdg_dir (
"CONFIG"),
"anklang", ckey);
176 emit_event (
"data", key);
177 return Path::stringwrite (filename, value.
as_string());
180 return GadgetImpl::set_data (ckey, value);
184ServerImpl::get_data (
const String &key)
const
186 const String ckey = canonify_key (key);
187 if (ckey.
size() && ckey[0] !=
'_')
189 const String filename = Path::join (Path::xdg_dir (
"CONFIG"),
"anklang", ckey);
190 return Path::stringread (filename);
193 return GadgetImpl::get_data (ckey);
200 return ServerImpl::instancep();
213 return FileCrawler::make_shared (cwd,
true,
false);
219 if (App.web_socket_server) {
220 String dir = App.web_socket_server->map_url (url);
222 return FileCrawler::make_shared (dir,
false,
false);
228Server::engine_stats ()
230 const String s =
"Unused";
231 printerr (
"Server::engine_stats:\n%s\n", s);
236Server::exit_program (
int status)
238 main_loop->quit (status);
244 label (label_), blurb (blurb_), notice (notice_), warning (warning_)
251 icon (icon_), label (label_), blurb (blurb_), notice (notice_), warning (warning_)
256Choice::Choice (IconString icon_,
String label_,
String blurb_) :
257 Choice (
"", icon_, label_, blurb_)
263 choices.push_back (std::move (newchoice));
275 case Error::NONE:
return _(
"OK");
277 case Error::INTERNAL:
return _(
"Internal error (please report)");
279 case Error::FILE_EOF:
return _(
"End of file");
280 case Error::FILE_OPEN_FAILED:
return _(
"Open failed");
281 case Error::FILE_SEEK_FAILED:
return _(
"Seek failed");
282 case Error::FILE_READ_FAILED:
return _(
"Read failed");
283 case Error::FILE_WRITE_FAILED:
return _(
"Write failed");
285 case Error::PARSE_ERROR:
return _(
"Parsing error");
286 case Error::NO_HEADER:
return _(
"Failed to detect header");
287 case Error::NO_SEEK_INFO:
return _(
"Failed to retrieve seek information");
288 case Error::NO_DATA_AVAILABLE:
return _(
"No data available");
289 case Error::DATA_CORRUPT:
return _(
"Data corrupt");
290 case Error::WRONG_N_CHANNELS:
return _(
"Wrong number of channels");
291 case Error::FORMAT_INVALID:
return _(
"Invalid format");
292 case Error::FORMAT_UNKNOWN:
return _(
"Unknown format");
293 case Error::DATA_UNMATCHED:
return _(
"Requested data values unmatched");
294 case Error::CODEC_FAILURE:
return _(
"Codec failure");
295 case Error::BROKEN_ARCHIVE:
return _(
"Broken archive");
296 case Error::BAD_PROJECT:
return _(
"Not a valid project");
297 case Error::NO_PROJECT_DIR:
return _(
"Missing project directory");
299 case Error::DEVICE_NOT_AVAILABLE:
return _(
"No device (driver) available");
300 case Error::DEVICE_ASYNC:
return _(
"Device not async capable");
301 case Error::DEVICE_BUSY:
return _(
"Device busy");
302 case Error::DEVICE_FORMAT:
return _(
"Failed to configure device format");
303 case Error::DEVICE_BUFFER:
return _(
"Failed to configure device buffer");
304 case Error::DEVICE_LATENCY:
return _(
"Failed to configure device latency");
305 case Error::DEVICE_CHANNELS:
return _(
"Failed to configure number of device channels");
306 case Error::DEVICE_FREQUENCY:
return _(
"Failed to configure device frequency");
307 case Error::DEVICES_MISMATCH:
return _(
"Device configurations mismatch");
309 case Error::WAVE_NOT_FOUND:
return _(
"No such wave");
310 case Error::UNIMPLEMENTED:
return _(
"Functionality not implemented");
311 case Error::INVALID_PROPERTY:
return _(
"Invalid object property");
312 case Error::INVALID_MIDI_CONTROL:
return _(
"Invalid MIDI control type");
313 case Error::OPERATION_BUSY:
return _(
"Operation already in prgress");
321ase_error_from_errno (
int sys_errno,
Error fallback)
323 if (sys_errno <
int (Error::INTERNAL))
324 return Error (sys_errno);
330ServerImpl::error_blurb (
Error error)
const
339 switch (musicaltuning)
342 case MusicalTuning::OD_12_TET:
343 return {
_(
"12 Tone Equal Temperament"),
344 _(
"The most common tuning system for modern Western music, "
345 "is the twelve-tone equal temperament, abbreviated as 12-TET, "
346 "which divides the octave into 12 equal parts.") };
347 case MusicalTuning::OD_7_TET:
348 return {
_(
"7 Tone Equal Temperament"),
349 _(
"A fairly common tuning system is the seven-tone equal temperament tuning system, "
350 "abbreviated as 7-TET. It divides the octave into 7 equal parts using 171 cent steps.") };
351 case MusicalTuning::OD_5_TET:
352 return {
_(
"5 Tone Equal Temperament"),
353 _(
"A fairly common tuning system is the five-tone equal temperament tuning system, "
354 "abbreviated as 5-TET. It divides the octave into 5 equal parts using 240 cent steps.") };
356 case MusicalTuning::DIATONIC_SCALE:
357 return {
_(
"Diatonic Scale"),
358 _(
"In music theory, a diatonic scale (also: heptatonia prima) is a seven-note "
359 "musical scale comprising five whole-tone and two half-tone steps. "
360 "The half tones are maximally separated, so between two half-tone steps "
361 "there are either two or three whole tones, repeating per octave.") };
362 case MusicalTuning::INDIAN_SCALE:
363 return {
_(
"Indian Scale"),
364 _(
"Diatonic scale used in Indian music with wolf interval at Dha, close to 3/2") };
365 case MusicalTuning::PYTHAGOREAN_TUNING:
366 return {
_(
"Pythagorean Tuning"),
367 _(
"Pythagorean tuning is the oldest way of tuning the 12-note chromatic scale, "
368 "in which the frequency relationships of all intervals are based on the ratio 3:2. "
369 "Its discovery is generally credited to Pythagoras.") };
370 case MusicalTuning::PENTATONIC_5_LIMIT:
371 return {
_(
"Pentatonic 5-limit"),
372 _(
"Pentatonic scales are used in modern jazz and pop/rock contexts "
373 "because they work exceedingly well over several chords diatonic "
374 "to the same key, often better than the parent scale.") };
375 case MusicalTuning::PENTATONIC_BLUES:
376 return {
_(
"Pentatonic Blues"),
377 _(
"The blues scale is the minor pentatonic with an additional augmented fourth, "
378 "which is referred to as the \"blues note\".") };
379 case MusicalTuning::PENTATONIC_GOGO:
380 return {
_(
"Pentatonic Gogo"),
381 _(
"The Pentatonic Gogo scale is an anhemitonic pentatonic scale used to tune the "
382 "instruments of the Gogo people of Tanzania.") };
384 case MusicalTuning::QUARTER_COMMA_MEANTONE:
385 return {
_(
"Quarter-Comma Meantone"),
386 _(
"Quarter-comma meantone was the most common meantone temperament in the "
387 "sixteenth and seventeenth centuries and sometimes used later.") };
388 case MusicalTuning::SILBERMANN_SORGE:
389 return {
_(
"Silbermann-Sorge Temperament"),
390 _(
"The Silbermann-Sorge temperament is a meantone temperament used for "
391 "Baroque era organs by Gottfried Silbermann.") };
393 case MusicalTuning::WERCKMEISTER_3:
394 return {
_(
"Werckmeister III"),
395 _(
"This tuning uses mostly pure (perfect) fifths, as in Pythagorean tuning, but each "
396 "of the fifths C-G, G-D, D-A and B-F# is made smaller, i.e. tempered by 1/4 comma. "
397 "Werckmeister designated this tuning as particularly suited for playing chromatic music.") };
398 case MusicalTuning::WERCKMEISTER_4:
399 return {
_(
"Werckmeister IV"),
400 _(
"In this tuning the fifths C-G, D-A, E-B, F#-C#, and Bb-F are tempered narrow by 1/3 comma, "
401 "and the fifths G#-D# and Eb-Bb are widened by 1/3 comma. The other fifths are pure. "
402 "Most of its intervals are close to sixth-comma meantone. "
403 "Werckmeister designed this tuning for playing mainly diatonic music.") };
404 case MusicalTuning::WERCKMEISTER_5:
405 return {
_(
"Werckmeister V"),
406 _(
"In this tuning the fifths D-A, A-E, F#-C#, C#-G#, and F-C are narrowed by 1/4 comma, "
407 "and the fifth G#-D# is widened by 1/4 comma. The other fifths are pure. "
408 "This temperament is closer to equal temperament than Werckmeister III or IV.") };
409 case MusicalTuning::WERCKMEISTER_6:
410 return {
_(
"Werckmeister VI"),
411 _(
"This tuning is also known as Septenarius tuning is based on a division of the monochord "
412 "length into 196 = 7 * 7 * 4 parts. "
413 "The resulting scale has rational frequency relationships, but in practice involves pure "
414 "and impure sounding fifths. "
415 "Werckmeister described the Septenarius as a \"temperament which has nothing at all to do "
416 "with the divisions of the comma, nevertheless in practice so correct that one can be really "
417 "satisfied with it\".") };
418 case MusicalTuning::KIRNBERGER_3:
419 return {
_(
"Kirnberger III"),
420 _(
"Kirnberger's method of compensating for and closing the circle of fifths is to split the \"wolf\" "
421 "interval known to those who have used meantone temperaments between four fifths instead, "
422 "allowing for four 1/4-comma wolves to take their place. "
423 "1/4-comma wolves are used extensively in meantone and are much easier to tune and to listen to. "
424 "Therefore, only one third remains pure (between C and E).") };
425 case MusicalTuning::YOUNG:
426 return {
_(
"Young Temperament"),
427 _(
"Thomas Young devised a form of musical tuning to make the harmony most perfect in those keys which "
428 "are the most frequently used (give better major thirds in those keys), but to not have any unplayable keys. "
429 "This is attempted by tuning upwards from C a sequence of six pure fourths, "
430 "as well as six equally imperfect fifths.") };
435static bool musical_tuning_info__ = EnumInfo::impl (musical_tuning_info);
438ServerImpl::musical_tuning_blurb (
MusicalTuning musicaltuning)
const
440 return EnumInfo::value_info (musicaltuning).blurb;
444ServerImpl::musical_tuning_label (
MusicalTuning musicaltuning)
const
446 return EnumInfo::value_info (musicaltuning).label;
452ServerImpl::user_note (
const String &text,
const String &channel, UserNote::Flags flags,
const String &rest)
454 UserNote unote { user_note_id++, flags, channel.empty() ?
"misc" : channel, text, rest };
457 this->emit_event (
"usernote",
"", vrec);
460 if (!unote.rest.empty())
461 s +=
" (" + unote.rest +
")";
473ServerImpl::telemem_allocate (
uint32 length)
const
475 return telemetry_arena.
allocate (length);
479ServerImpl::telemem_release (Block telememblock)
const
481 telemetry_arena.
release (telememblock);
485ServerImpl::telemem_start ()
const
491validate_telemetry_segments (
const TelemetrySegmentS &segments,
size_t *payloadlength)
494 const TelemetrySegment *last =
nullptr;
495 for (
const auto &seg : segments)
497 if (last && seg.offset < last->offset + last->length)
499 if (seg.offset < 0 || (seg.offset & 3) || seg.length <= 0 || (seg.length & 3) ||
500 size_t (seg.offset + seg.length) > telemetry_size)
502 *payloadlength += seg.length;
508ASE_CLASS_DECLS (TelemetryPlan);
511 int32 interval_ms_ = -1;
512 LoopID timerid_ = LoopID::INVALID;
514 TelemetrySegmentS segments_;
515 const char *telemem_ =
nullptr;
517 void send_telemetry();
518 void setup (
const char *start,
size_t payloadlength,
const TelemetrySegmentS &plan,
int32 interval_ms);
526 size_t payloadlength = 0;
527 if (!validate_telemetry_segments (segments, &payloadlength))
529 warning (
"%s: invalid segment list",
"Ase::ServerImpl::broadcast_telemetry");
535 warning (
"%s: cannot broadcast telemetry without jsonapi connection",
"Ase::ServerImpl::broadcast_telemetry");
543 tplan->send_blob_ = jsonapi_connection_sender();
545 tplan->setup ((
const char*) telemetry_arena.
location(), payloadlength, segments, interval_ms);
550TelemetryPlan::setup (
const char *start,
size_t payloadlength,
const TelemetrySegmentS &segments,
int32 interval_ms)
552 if (timerid_ == LoopID::INVALID || interval_ms_ != interval_ms)
554 if (timerid_ != LoopID::INVALID)
555 main_loop->cancel (timerid_);
557 auto send_telemetry = [tplan] () { tplan->send_telemetry();
return true; };
558 interval_ms_ = interval_ms;
559 timerid_ = interval_ms <= 0 || segments.empty() ? LoopID::INVALID : main_loop->add (send_telemetry,
std::chrono::milliseconds (interval_ms));
561 if (timerid_ != LoopID::INVALID)
564 segments_ = segments;
565 payload_.
resize (payloadlength);
576TelemetryPlan::send_telemetry ()
579 if (!telemem_ || segments_.empty() || payload_.
empty())
581 char *
data = &payload_[0];
583 for (
const auto &seg : segments_)
585 memcpy (data + datapos, telemem_ + seg.offset, seg.length);
586 datapos += seg.length;
589 (void) send_blob_ (payload_);
592TelemetryPlan::~TelemetryPlan()
594 if (timerid_ != LoopID::INVALID)
597 if (!main_loop->has_quit())
598 main_loop->cancel (timerid_);
599 timerid_ = LoopID::INVALID;
DataListContainer - typesafe storage and retrieval of arbitrary members.
T get_custom_data(CustomDataKey< T > *key) const
Retrieve contents of the custom keyed data member, returns DataKey::fallback if nothing was set.
void set_custom_data(CustomDataKey< T > *key, T data)
Assign data to the custom keyed data member, deletes any previously set data.
CustomDataKey objects are used to identify and manage custom data members of CustomDataContainer obje...
bool broadcast_telemetry(const TelemetrySegmentS &plan, int32 interval_ms) override
Broadcast telemetry memory segments to the current Jsonipc connection.
Central singleton, serves as API entry point.
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
#define _(...)
Retrieve the translation of a C or C++ string.
The Anklang C++ API namespace.
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
bool has_ui_tests
Whether any UI tests are pending.
String string_to_identifier(const String &input)
Force lower case, alphanumerics + underscore and non-digit start.
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
int32_t int32
A 32-bit signed integer.
bool json_parse(const String &jsonstring, T &target)
Parse a well formed JSON string and assign contents to target.
Error
Enum representing Error states.
bool auto_exit
Whether to auto-exit after tests complete.
const char * ase_error_blurb(Error error)
Describe Error condition.
String string_replace(const String &input, const String &marker, const String &replacement, size_t maxn)
Replace substring marker in input with replacement, at most maxn times.
ChoiceS & operator+=(ChoiceS &choices, Choice &&newchoice)
Convenience ChoiceS construction helper.
String program_alias()
Retrieve the program name as used for logging or debug messages.
const char * ase_version()
Provide a string containing the package version.
std::string String
Convenience alias for std::string.
uint32_t uint32
A 32-bit unsigned integer.
uint32_t uint
Provide 'uint' as convenience type.
String json_stringify(const T &source, Writ::Flags flags=Writ::Flags(0))
Create JSON string from source.
MusicalTuning
Musical tunings, see: http://en.wikipedia.org/wiki/Musical_tuning.
const char * ase_build_id()
Provide a string containing the ASE library build id.
Configuration values for the UI.
unsigned long long uint64
T shared_from_this(T... args)
Representation of one possible choice for selection properties.
uint64 location() const
Address of memory area.
Block allocate(uint32 length) const
Create a memory block from cache-line aligned memory area, MT-Unsafe.
void release(Block allocatedblock) const
Realease a previously allocated block, MT-Unsafe.
Value type used to interface with various property types.
String as_string() const
Convert Value to a string, not very useful for RECORD or ARRAY.