├── GNUmakefile ├── LICENSE ├── Makefile.mk ├── NEWS.md ├── README.md ├── ase ├── GNUmakefile ├── Makefile.mk ├── api.hh ├── atomics.cc ├── atomics.hh ├── benchmarks.cc ├── blob.cc ├── blob.hh ├── callback.cc ├── callback.hh ├── clapdevice.cc ├── clapdevice.hh ├── clapplugin.cc ├── clapplugin.hh ├── clip.cc ├── clip.hh ├── combo.cc ├── combo.hh ├── compress.cc ├── compress.hh ├── crawler.cc ├── crawler.hh ├── cxxaux.cc ├── cxxaux.hh ├── datautils.cc ├── datautils.hh ├── dbus.cc ├── dbus.hh ├── defs.hh ├── device.cc ├── device.hh ├── driver-alsa.cc ├── driver-jack.cc ├── driver.cc ├── driver.hh ├── engine.cc ├── engine.hh ├── entropy.cc ├── entropy.hh ├── eventlist.cc ├── eventlist.hh ├── formatter.cc ├── formatter.hh ├── gadget.cc ├── gadget.hh ├── gtk2wrap.cc ├── gtk2wrap.hh ├── inifile.cc ├── inifile.hh ├── internal.hh ├── jsonapi.cc ├── jsonapi.hh ├── levenshtein.cc ├── levenshtein.hh ├── liquidsfz.cc ├── liquidsfzapi.cc ├── loft.cc ├── loft.hh ├── logging.cc ├── logging.hh ├── loop.cc ├── loop.hh ├── main.cc ├── main.hh ├── mathtables.cc ├── mathutils.cc ├── mathutils.hh ├── member.cc ├── member.hh ├── memory.cc ├── memory.hh ├── midievent.cc ├── midievent.hh ├── midilib.cc ├── midilib.hh ├── mime-types.hh ├── minizip.c ├── minizip.h ├── monitor.cc ├── monitor.hh ├── nativedevice.cc ├── nativedevice.hh ├── object.cc ├── object.hh ├── parameter.cc ├── parameter.hh ├── path.cc ├── path.hh ├── platform.cc ├── platform.hh ├── processor.cc ├── processor.hh ├── project.cc ├── project.hh ├── properties.cc ├── properties.hh ├── queuemux.cc ├── queuemux.hh ├── randomhash.cc ├── randomhash.hh ├── regex.cc ├── regex.hh ├── serialize.cc ├── serialize.hh ├── server.cc ├── server.hh ├── signalmath.cc ├── signalmath.hh ├── sndfile.cc ├── sortnet.cc ├── sortnet.hh ├── storage.cc ├── storage.hh ├── strings.cc ├── strings.hh ├── testing.cc ├── testing.hh ├── track.cc ├── track.hh ├── transport.cc ├── transport.hh ├── unicode.cc ├── unicode.hh ├── utils.cc ├── utils.hh ├── value.cc ├── value.hh ├── wave.cc ├── wave.hh ├── websocket.cc └── websocket.hh ├── devices ├── GNUmakefile ├── Makefile.mk ├── blepsynth │ ├── GNUmakefile │ ├── Makefile.mk │ ├── bleposc.hh │ ├── bleposcdata.cc │ ├── blepsynth.cc │ ├── laddervcf.hh │ ├── linearsmooth.hh │ └── skfilter.hh ├── colorednoise.cc ├── freeverb │ ├── GNUmakefile │ ├── Makefile.mk │ ├── allpass.cpp │ ├── allpass.hpp │ ├── comb.cpp │ ├── comb.hpp │ ├── denormals.h │ ├── freeverb.cc │ ├── readme.txt │ ├── revmodel.cpp │ ├── revmodel.hpp │ └── tuning.h ├── liquidsfz │ ├── Makefile.mk │ └── liquidsfz.cc └── saturation │ ├── Makefile.mk │ ├── saturation.cc │ └── saturationdsp.hh ├── doc ├── GNUmakefile ├── Makefile.mk ├── NAV.md ├── anklang.1.md ├── ch-appendix.md ├── ch-development.md ├── ch-releasing.md ├── ch-scripting.md ├── copyright ├── copyright.ini ├── filt-docs2.py ├── filt-man.py ├── highlights.theme ├── how-audio.md ├── index.md ├── javascript │ └── mathjax.js ├── jsdoc-slashes.cjs ├── jsdoc2md.js ├── jsdocrc.json ├── mkdocs.yml ├── pandoc-pdf.tex ├── style │ ├── Makefile.mk │ ├── doxyextra.css │ ├── faketex.scss │ ├── features.scss │ ├── mathjax-config.js │ ├── mkdocs.css │ └── onload.html ├── template.diff └── tut-play.md ├── electron ├── Makefile.mk ├── main.js └── preload.js ├── images ├── README.md ├── anklangicons.sh ├── icons │ ├── engine0.svg │ ├── engine1.svg │ ├── folder.svg │ └── menumore.svg └── knobs │ ├── Makefile.mk │ ├── cknob.svg │ └── mksprite.py ├── jsonipc ├── Makefile ├── cxxjip.py ├── jsonipc.hh ├── jsonipc.js └── testjsonipc.cc ├── misc ├── AppRun ├── Dockerfile.arch ├── Dockerfile.noble ├── GNUmakefile ├── Makefile.mk ├── anklang-mime.xml ├── anklang.desktop ├── blame-lines ├── checkcrlist.py ├── checkinsertions.sh ├── colorize.sh ├── config-checks.mk ├── config-external.mk ├── config-uname.mk ├── config-utils.mk ├── doxygen.py ├── mkAppImage.sh ├── mkassets.sh ├── mkcopyright.py ├── mkdeb.sh ├── mknews.sh ├── publish.sh ├── subdirs.mk ├── synsmell.ts └── version.sh ├── package.json ├── rand └── hotspots.sh ├── ui ├── GNUmakefile ├── Makefile.mk ├── assets │ ├── favicon.svg │ ├── spin-arrows.svg │ └── spinner.svg ├── b │ ├── GNUmakefile │ ├── aboutdialog.js │ ├── app.js │ ├── basics.js │ ├── buttonbar.js │ ├── choiceinput.js │ ├── cliplist.js │ ├── clipview.js │ ├── contextmenu.js │ ├── crawlerdialog.js │ ├── databubble.js │ ├── deviceeditor.js │ ├── devicepanel.js │ ├── dialog.vue │ ├── editable.js │ ├── envue.js │ ├── icon.js │ ├── knob.js │ ├── menubar.js │ ├── menurow.js │ ├── menuseparator.js │ ├── menutitle.js │ ├── more.js │ ├── noticeboard.js │ ├── numberinput.js │ ├── objecteditor.js │ ├── partlist.js │ ├── piano-ctrl.js │ ├── pianoroll.js │ ├── playcontrols.js │ ├── positionview.js │ ├── preferencesdialog.vue │ ├── propgroup.js │ ├── shell.vue │ ├── statusbar.js │ ├── switchinput.js │ ├── textinput.js │ ├── toggle.js │ ├── tracklist.js │ ├── trackview.js │ └── treebrowser.js ├── ch-component.md ├── colors.js ├── cursors │ ├── cross.svg │ ├── cursors.css │ ├── eraser.svg │ ├── hresize.svg │ ├── knife.svg │ ├── move.svg │ └── pen.svg ├── dark.scss ├── dom.js ├── eslintrc.js ├── global.scss ├── grepdialog.js ├── host.js ├── index.html ├── jsextract.js ├── kbd.js ├── little.js ├── mixins.scss ├── mouse.js ├── palette.scss ├── postcss.config.mjs ├── script.js ├── sfc-compile.js ├── signal.js ├── startup.js ├── strings.js ├── stylelintrc.cjs ├── tailwind.scss ├── theme.scss ├── tsconfig.json ├── types.d.ts ├── util.js ├── wrapper.js └── xbcomments.js └── x11test ├── epuppeteer.mjs ├── play-notes.json ├── replay.sh ├── trackrename.json └── x11rec.sh /GNUmakefile: -------------------------------------------------------------------------------- 1 | include Makefile.mk 2 | -------------------------------------------------------------------------------- /ase/GNUmakefile: -------------------------------------------------------------------------------- 1 | include ../misc/subdirs.mk 2 | -------------------------------------------------------------------------------- /ase/blob.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_BLOB_HH__ 3 | #define __ASE_BLOB_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | class BlobImpl; 10 | 11 | /// Binary large object storage container. 12 | class Blob { 13 | std::shared_ptr implp_; 14 | static Blob from_res (const char *resource); 15 | static Blob from_string (const String &name, const String &data); 16 | explicit Blob (std::shared_ptr blobimpl); 17 | public: 18 | String name (); ///< Retrieve the Blob's filename or url. 19 | const char* data (); ///< Retrieve the Blob's data. 20 | const uint8* bytes (); ///< Retrieve the Blob's data as uint8 buffer. 21 | size_t size (); ///< Retrieve the Blob's data size in bytes. 22 | String string (); ///< Copy Blob data into a zero terminated string. 23 | explicit Blob (); ///< Construct an empty Blob. 24 | explicit Blob (const String &auto_url); ///< Construct Blob from url or filename (auto detected). 25 | static Blob from_file (const String &filename); ///< Create Blob by loading from @a filename. 26 | static Blob from_url (const String &url); ///< Create Blob by opening a @a url. 27 | explicit operator bool () const; ///< Checks if the Blob contains accessible data. 28 | }; 29 | 30 | // == Helpers == 31 | uint8* zintern_decompress (unsigned int decompressed_size, const unsigned char *cdata, unsigned int cdata_size); 32 | void zintern_free (uint8 *dc_data); 33 | 34 | } // Ase 35 | 36 | #endif // __ASE_BLOB_HH__ 37 | -------------------------------------------------------------------------------- /ase/callback.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "callback.hh" 3 | #include "internal.hh" 4 | #include 5 | 6 | #define CDEBUG(...) Ase::debug ("callback", __VA_ARGS__) 7 | 8 | namespace Ase { 9 | 10 | // == RtCall::Callable == 11 | RtCall::~RtCall () 12 | { 13 | memset (mem_, 0, sizeof (mem_)); 14 | } 15 | 16 | RtCall::RtCall (const RtCall &other) 17 | { 18 | if (other.callable()) 19 | memcpy (mem_, other.mem_, sizeof (other.mem_)); 20 | } 21 | 22 | const RtCall::Callable* 23 | RtCall::callable () const 24 | { 25 | if (mem_[0] == 0 && mem_[1] == 0 && mem_[2] == 0 && mem_[3] == 0) 26 | return nullptr; 27 | return reinterpret_cast (unalias_ptr (&mem_[0])); 28 | } 29 | 30 | RtCall::RtCall (void (*f) ()) 31 | { 32 | struct Wrapper : Callable { 33 | Wrapper (void (*f) ()) : f_ (f) {} 34 | void call() const override { return f_ (); } 35 | void (*f_) (); 36 | }; 37 | static_assert (sizeof (mem_) >= sizeof (Wrapper)); 38 | Wrapper *w = new (mem_) Wrapper { f }; 39 | ASE_ASSERT_RETURN (w == (void*) mem_); 40 | } 41 | 42 | void 43 | RtCall::invoke () 44 | { 45 | const Callable *wcallable = callable(); 46 | if (wcallable) 47 | wcallable->call(); 48 | } 49 | 50 | void RtCall::Callable::call () const {} 51 | 52 | // == JobQueue == 53 | JobQueue::JobQueue (const Caller &caller) : 54 | caller_ (caller) 55 | {} 56 | 57 | } // Ase 58 | 59 | #include 60 | 61 | namespace { 62 | using namespace Ase; 63 | 64 | TEST_INTEGRITY (callback_list_test); 65 | static void 66 | callback_list_test() 67 | { 68 | std::shared_ptr> cbl = CallbackList::make_shared(); 69 | String r; 70 | const size_t aid = cbl->add ([&r] (const char *delim) { r += delim; r += "a"; }); 71 | const size_t bid = cbl->add ([&r,&cbl,aid] (const char *delim) { r += delim; r += "b"; cbl->del (aid); }); 72 | const size_t cid = cbl->add ([&r] (const char *delim) { r += delim; r += "c"; }); 73 | (*cbl) ("+"); 74 | assert (r == "+a+b+c"); 75 | (*cbl) ("|"); 76 | assert (r == "+a+b+c|b|c"); 77 | cbl->del (bid); 78 | (*cbl) ("*"); 79 | assert (r == "+a+b+c|b|c*c"); 80 | cbl->del (cid); 81 | (*cbl) ("-"); 82 | assert (r == "+a+b+c|b|c*c"); 83 | } 84 | 85 | } // Anon 86 | -------------------------------------------------------------------------------- /ase/clapdevice.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_CLAP_DEVICE_HH__ 3 | #define __ASE_CLAP_DEVICE_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | class ClapDeviceImpl : public DeviceImpl { 10 | ASE_DEFINE_MAKE_SHARED (ClapDeviceImpl); 11 | ClapPluginHandleP handle_; 12 | Connection paramschange_; 13 | protected: 14 | virtual ~ClapDeviceImpl (); 15 | void serialize (WritNode &xs) override; 16 | void proc_params_change (const Event &event); 17 | void _set_parent (GadgetImpl *parent) override; 18 | void _activate () override; 19 | public: 20 | explicit ClapDeviceImpl (ClapPluginHandleP claphandle); 21 | static DeviceInfoS list_clap_plugins (); 22 | DeviceInfo device_info () override; 23 | PropertyS access_properties () override; 24 | void gui_toggle () override; 25 | bool gui_supported () override; 26 | bool gui_visible () override; 27 | AudioProcessorP _audio_processor () const override; 28 | void _set_event_source (AudioProcessorP esource) override; 29 | void _disconnect_remove () override; 30 | String get_device_path (); 31 | static String clap_version (); 32 | static DeviceP create_clap_device (AudioEngine &engine, const String &clapuri); 33 | static ClapPluginHandleP access_clap_handle (DeviceP device); 34 | friend struct ClapPropertyImpl; 35 | }; 36 | 37 | } // Ase 38 | 39 | #endif // __ASE_CLAP_DEVICE_HH__ 40 | -------------------------------------------------------------------------------- /ase/combo.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_COMBO_HH__ 3 | #define __ASE_COMBO_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | class AudioCombo : public AudioProcessor, protected ProcessorManager { 10 | protected: 11 | AudioProcessorS processors_; 12 | AudioProcessorP eproc_; 13 | virtual void reconnect (size_t index, bool insertion) = 0; 14 | explicit AudioCombo (const ProcessorSetup&); 15 | virtual ~AudioCombo (); 16 | public: 17 | void insert (AudioProcessorP proc, ssize_t pos = ~size_t (0)); 18 | bool remove (AudioProcessor &proc); 19 | AudioProcessorP at (uint nth); 20 | ssize_t find_pos (AudioProcessor &proc); 21 | size_t size (); 22 | void set_event_source (AudioProcessorP eproc); 23 | AudioProcessorS list_processors () const; 24 | }; 25 | 26 | class AudioChain : public AudioCombo { 27 | ASE_CLASS_DECLS (Inlet); 28 | const SpeakerArrangement ispeakers_ = SpeakerArrangement (0); 29 | const SpeakerArrangement ospeakers_ = SpeakerArrangement (0); 30 | InletP inlet_; 31 | AudioProcessor *last_output_ = nullptr; 32 | protected: 33 | void initialize (SpeakerArrangement busses) override; 34 | void reset (uint64 target_stamp) override; 35 | void render (uint n_frames) override; 36 | uint schedule_children () override; 37 | void reconnect (size_t index, bool insertion) override; 38 | uint chain_up (AudioProcessor &pfirst, AudioProcessor &psecond); 39 | public: 40 | explicit AudioChain (const ProcessorSetup&, SpeakerArrangement iobuses = SpeakerArrangement::STEREO); 41 | virtual ~AudioChain (); 42 | struct Probe { float dbspl = -192; }; 43 | using ProbeArray = std::array; 44 | ProbeArray* run_probes (bool enable); 45 | static void static_info (AudioProcessorInfo &info); 46 | private: 47 | ProbeArray *probes_ = nullptr; 48 | bool probes_enabled_ = false; 49 | FastMemory::Block probe_block_; 50 | }; 51 | 52 | } // Ase 53 | 54 | #endif // __ASE_COMBO_HH__ 55 | -------------------------------------------------------------------------------- /ase/compress.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_COMPRESS_HH__ 3 | #define __ASE_COMPRESS_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | bool is_aiff (const String &input); 10 | bool is_midi (const String &input); 11 | bool is_pdf (const String &input); 12 | bool is_wav (const String &input); 13 | 14 | bool is_compressed (const String &input); 15 | 16 | bool is_arj (const String &input); 17 | bool is_avi (const String &input); 18 | bool is_gz (const String &input); 19 | bool is_isz (const String &input); 20 | bool is_jpg (const String &input); 21 | bool is_lz4 (const String &input); 22 | bool is_ogg (const String &input); 23 | bool is_png (const String &input); 24 | bool is_xz (const String &input); 25 | bool is_zip (const String &input); 26 | 27 | 28 | String blake3_hash_file (const String &filename); 29 | String blake3_hash_string (const String &input); 30 | 31 | bool is_zstd (const String &input); 32 | String zstd_compress (const String &input, int level = 0); 33 | String zstd_compress (const void *src, size_t src_size, int level = 0); 34 | String zstd_uncompress (const String &input); 35 | ssize_t zstd_uncompress (const String &input, void *dst, size_t dst_size); 36 | ssize_t zstd_target_size (const String &input); 37 | 38 | StreamWriterP stream_writer_zstd (const StreamWriterP &ostream, int level = 0); 39 | StreamReaderP stream_reader_zstd (StreamReaderP &istream); 40 | 41 | } // Ase 42 | 43 | #endif // __ASE_COMPRESS_HH__ 44 | -------------------------------------------------------------------------------- /ase/crawler.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_CRAWLER_HH__ 3 | #define __ASE_CRAWLER_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | /// Class implementing a file system crawler. 10 | class FileCrawler final : public ObjectImpl, public virtual ResourceCrawler { 11 | String cwd_; 12 | const uint constraindir_ : 1; 13 | const uint constrainfile_ : 1; 14 | FileCrawler (const String &cwd, bool constraindir = false, bool constrainfile = false); 15 | protected: 16 | String2 assign_ (const String &utf8path, bool existingfile, bool notify = true); 17 | bool folder_ (const Resource *n, Resource *q) override; 18 | bool entries_ (const ResourceS *n, ResourceS *q) override; 19 | public: 20 | ASE_DEFINE_MAKE_SHARED (FileCrawler); 21 | ResourceS list_entries () override; 22 | Resource current_folder () override; 23 | String2 assign (const String &utf8path, 24 | bool existingfile) override { return assign_ (utf8path, existingfile, true); } 25 | Resource canonify (const String &utf8cwd, const String &utf8fragment, bool constraindir, bool constrainfile) override; 26 | String canonify_fspath (const String &fspath, const String &fsfragment, bool constraindir, bool constrainfile); 27 | String expand_fsdir (const String &fsdir); 28 | }; 29 | 30 | } // Ase 31 | 32 | #endif // __ASE_CRAWLER_HH__ 33 | -------------------------------------------------------------------------------- /ase/datautils.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "datautils.hh" 3 | #include "internal.hh" 4 | 5 | namespace Ase { 6 | 7 | float const_float_zeros[AUDIO_BLOCK_FLOAT_ZEROS_SIZE] = { 0, /*...*/ }; 8 | 9 | float 10 | square_sum (uint n_values, const float *ivalues) 11 | { 12 | float accu = 0.0; 13 | for (uint i = 0; i < n_values; i++) 14 | accu += ivalues[i] * ivalues[i]; 15 | return accu; 16 | } 17 | 18 | float 19 | square_max (uint n_values, const float *ivalues) 20 | { 21 | float accu = 0.0; 22 | for (uint i = 0; i < n_values; i++) 23 | accu = std::max (accu, ivalues[i] * ivalues[i]); 24 | return accu; 25 | } 26 | 27 | } // Ase 28 | -------------------------------------------------------------------------------- /ase/datautils.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_DATAUTILS_HH__ 3 | #define __ASE_DATAUTILS_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | /// Maximum number of values in the const_float_zeros block. 10 | constexpr const uint AUDIO_BLOCK_FLOAT_ZEROS_SIZE = 16384; 11 | 12 | /// Block of const floats allof value 0. 13 | extern float const_float_zeros[AUDIO_BLOCK_FLOAT_ZEROS_SIZE]; 14 | 15 | /// Calculate suqare sum of a block of floats. 16 | float square_sum (uint n_values, const float *ivalues); 17 | 18 | /// Find the maximum suqared value in a block of floats. 19 | float square_max (uint n_values, const float *ivalues); 20 | 21 | // Convert integer to float samples. 22 | template inline void convert_samples (size_t n, S *src, D *dst, uint16 byte_order); 23 | 24 | // Convert float to integer samples with clipping. 25 | template inline void convert_clip_samples (size_t n, S *src, D *dst, uint16 byte_order); 26 | 27 | /// Fill `n` values of `dst` with `f`. 28 | extern inline void 29 | floatfill (float *dst, float f, size_t n) 30 | { 31 | for (size_t i = 0; i < n; i++) 32 | dst[i] = f; 33 | } 34 | 35 | /// Copy a block of floats 36 | extern inline void 37 | fast_copy (size_t n, float *d, const float *s) 38 | { 39 | static_assert (sizeof (float) == 4, ""); 40 | static_assert (sizeof (wchar_t) == 4, ""); 41 | wmemcpy ((wchar_t*) d, (const wchar_t*) s, n); 42 | } 43 | 44 | /// Copy a block of integers 45 | extern inline void 46 | fast_copy (size_t n, uint32_t *d, const uint32_t *s) 47 | { 48 | static_assert (sizeof (uint32_t) == 4, ""); 49 | static_assert (sizeof (wchar_t) == 4, ""); 50 | wmemcpy ((wchar_t*) d, (const wchar_t*) s, n); 51 | } 52 | 53 | // == Implementations == 54 | template<> inline void 55 | convert_samples (size_t n, const int16_t *src, float *dst, uint16 byte_order) 56 | { 57 | ASE_ASSERT_RETURN (__BYTE_ORDER__ == byte_order); // swapping __BYTE_ORDER__ not implemented 58 | const auto bound = dst + n; 59 | while (dst < bound) 60 | *dst++ = *src++ * (1. / 32768.); 61 | } 62 | 63 | template<> inline void 64 | convert_clip_samples (size_t n, const float *src, int16_t *dst, uint16 byte_order) 65 | { 66 | ASE_ASSERT_RETURN (__BYTE_ORDER__ == byte_order); // swapping __BYTE_ORDER__ not implemented 67 | static_assert (int16_t (0.99999990F * 32768.F) == 32767); 68 | const auto bound = src + n; 69 | while (src < bound) 70 | { 71 | auto v = *src++; 72 | v = std::min (0.99999990F, std::max (v, -1.0F)); 73 | *dst++ = v * 32768.; 74 | } 75 | } 76 | 77 | } // Ase 78 | 79 | #endif // __ASE_DATAUTILS_HH__ 80 | -------------------------------------------------------------------------------- /ase/dbus.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_DBUS_HH__ 3 | #define __ASE_DBUS_HH__ 4 | 5 | #include 6 | 7 | namespace Ase::DBus { 8 | 9 | String rtkit_make_high_priority (pid_t thread, int nice_level); 10 | int rtkit_get_min_nice_level (); 11 | 12 | } // Ase::DBus 13 | 14 | #endif // __ASE_DBUS_HH__ 15 | -------------------------------------------------------------------------------- /ase/device.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "device.hh" 3 | #include "internal.hh" 4 | 5 | namespace Ase { 6 | 7 | // == Device == 8 | Device::Device() : 9 | devs (this, "devs") 10 | {} 11 | 12 | bool 13 | Device::devs_ (const DeviceS *n, DeviceS *q) 14 | { 15 | if (n) { 16 | // no assignments 17 | // devs.notify(); 18 | } 19 | if (q) 20 | *q = list_devices(); 21 | return true; 22 | } 23 | 24 | // == DeviceImpl == 25 | void 26 | DeviceImpl::_set_parent (GadgetImpl *parent) 27 | { 28 | assert_warn (!is_active()); 29 | GadgetImpl::_set_parent (parent); 30 | } 31 | 32 | void 33 | DeviceImpl::_activate() 34 | { 35 | assert_return (!activated_); 36 | activated_ = true; 37 | } 38 | 39 | void 40 | DeviceImpl::_deactivate() 41 | { 42 | assert_return (activated_); 43 | activated_ = false; 44 | } 45 | 46 | template std::pair,ssize_t> 47 | find_shared_by_ref (const std::vector > &v, const E &e) 48 | { 49 | for (ssize_t i = 0; i < v.size(); i++) 50 | if (&e == &*v[i]) 51 | return std::make_pair (v[i], i); 52 | return std::make_pair (std::shared_ptr{}, -1); 53 | } 54 | 55 | void 56 | DeviceImpl::_disconnect_remove () 57 | { 58 | AudioProcessorP proc = _audio_processor(); 59 | return_unless (proc); 60 | AudioEngine *engine = &proc->engine(); 61 | auto job = [proc] () { 62 | proc->enable_engine_output (false); 63 | proc->disconnect_ibuses(); 64 | proc->disconnect_obuses(); 65 | proc->disconnect_event_input(); 66 | // TODO: do we need to remove this from parent container (combo)? 67 | }; 68 | engine->async_jobs += job; 69 | } 70 | 71 | DeviceInfo 72 | DeviceImpl::extract_info (const String &aseid, const AudioProcessor::StaticInfo &static_info) 73 | { 74 | AudioProcessorInfo pinfo; 75 | static_info (pinfo); 76 | DeviceInfo info = { 77 | .uri = aseid, 78 | .name = pinfo.label, 79 | .category = pinfo.category, 80 | .description = pinfo.description, 81 | .website_url = pinfo.website_url, 82 | .creator_name = pinfo.creator_name, 83 | .creator_url = pinfo.creator_url, 84 | }; 85 | return info; 86 | } 87 | 88 | // == Device == 89 | void 90 | Device::remove_self () 91 | { 92 | Gadget *parent = _parent(); 93 | NativeDevice *device = dynamic_cast (parent); 94 | if (device) 95 | device->remove_device (*this); 96 | } 97 | 98 | Track* 99 | Device::_track () const 100 | { 101 | for (Gadget *parent = _parent(); parent; parent = parent->_parent()) 102 | { 103 | Track *track = dynamic_cast (parent); 104 | if (track) 105 | return track; 106 | } 107 | return nullptr; 108 | } 109 | 110 | } // Ase 111 | -------------------------------------------------------------------------------- /ase/device.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_DEVICE_HH__ 3 | #define __ASE_DEVICE_HH__ 4 | 5 | #include 6 | #include 7 | 8 | namespace Ase { 9 | 10 | class DeviceImpl : public GadgetImpl, public virtual Device { 11 | bool activated_ = false; 12 | protected: 13 | explicit DeviceImpl () {} // abstract base 14 | void _set_parent (GadgetImpl *parent) override; 15 | public: 16 | void _activate () override; 17 | void _deactivate () override; 18 | DeviceS list_devices () override { return {}; } 19 | bool is_active () override { return activated_; } 20 | bool gui_supported () override { return false; } 21 | bool gui_visible () override { return false; } 22 | void gui_toggle () override {} 23 | void _disconnect_remove () override; 24 | static DeviceInfo extract_info (const String &aseid, const AudioProcessor::StaticInfo &static_info); 25 | }; 26 | 27 | } // Ase 28 | 29 | #endif // __ASE_DEVICE_HH__ 30 | -------------------------------------------------------------------------------- /ase/entropy.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_ENTROPY_HH__ 3 | #define __ASE_ENTROPY_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | /// Collect entropy from the current process, usually quicker than collect_system_entropy(). 10 | void collect_runtime_entropy (uint64 *data, size_t n); 11 | 12 | /// Collect entropy from system devices, like interrupt counters, clocks and random devices. 13 | void collect_system_entropy (uint64 *data, size_t n); 14 | 15 | } // Ase 16 | 17 | #endif // __ASE_ENTROPY_HH__ 18 | -------------------------------------------------------------------------------- /ase/eventlist.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "eventlist.hh" 3 | #include "utils.hh" 4 | #include "testing.hh" 5 | #include "internal.hh" 6 | 7 | TEST_INTEGRITY (event_list_tests); 8 | static void 9 | event_list_tests() 10 | { 11 | struct Control { 12 | uint tick = 0; 13 | Control (uint t = 0) : tick (t) {} 14 | static int 15 | compare_order (const Control &a, const Control &b) 16 | { 17 | return Ase::Aux::compare_lesser (a.tick, b.tick); 18 | } 19 | }; 20 | struct Note : Control { 21 | uint16_t key = 0; 22 | Note (uint t = 0, uint k = 0) : Control (t), key (k) {} 23 | static int 24 | compare_order (const Note &a, const Note &b) 25 | { 26 | int cmp = Control::compare_order (a, b); 27 | if (!cmp) 28 | cmp = Ase::Aux::compare_lesser (a.key, b.key); 29 | return cmp; 30 | } 31 | }; 32 | const Note *cnote; 33 | int ret; 34 | 35 | struct CompareKey { int operator() (const Note &a, const Note &b) { return int (a.key) - int (b.key); } }; 36 | struct CompareTick { int operator() (const Note &a, const Note &b) { return Note::compare_order (a, b); } }; 37 | using OrderedNoteList = Ase::OrderedEventList; 38 | OrderedNoteList::ConstP notesp; 39 | { 40 | int modified = -99; 41 | Ase::EventList note_events ([&] (const Note&, int mod) { modified = mod; }); 42 | note_events.insert (Note()); TASSERT (modified == +1); // inserted 43 | note_events.insert (Note()); TASSERT (modified == 0); // replaced 44 | cnote = note_events.lookup (Note()); TASSERT (cnote != nullptr); 45 | ret = note_events.remove (Note()); TASSERT (ret && modified == -1); // removed 46 | modified = -99; 47 | ret = note_events.remove (Note()); TASSERT (!ret && modified == -99); // unknown 48 | note_events.insert (Note (33, 3)); TASSERT (modified == +1); // inserted 49 | cnote = note_events.first(); TASSERT (cnote && cnote->key == 3); 50 | note_events.insert (Note (15, 5)); TASSERT (modified == +1); // inserted 51 | note_events.insert (Note (21, 1)); TASSERT (modified == +1); // inserted 52 | cnote = note_events.first(); TASSERT (cnote && cnote->key == 1); 53 | ret = note_events.last() - cnote + 1; TASSERT (ret == 3); 54 | cnote = note_events.last(); TASSERT (cnote && cnote->key == 5); 55 | cnote = note_events.lookup_after (Note (0, 2)); TASSERT (cnote && cnote->key == 3); 56 | cnote += 1; TASSERT (cnote && cnote->key == 5); 57 | notesp = note_events.ordered_events(); 58 | note_events.clear_silently(); 59 | ret = note_events.size(); TASSERT (ret == 0); 60 | } 61 | 62 | const auto ¬es = *notesp; 63 | TASSERT (notes.size() == 3); 64 | TASSERT (notes[0].tick == 15); 65 | TASSERT (notes[1].tick == 21); 66 | TASSERT (notes[2].tick == 33); 67 | cnote = notes.lookup (Note (33, 3)); TASSERT (cnote && cnote == ¬es.back()); 68 | cnote = notes.lookup_after (Note (17, 0)); TASSERT (cnote && cnote->key == 1); 69 | cnote = notes.lookup_after (Note (0, 0)); TASSERT (cnote && cnote == ¬es.front()); 70 | } 71 | -------------------------------------------------------------------------------- /ase/formatter.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_FORMATTER_HH__ 3 | #define __ASE_FORMATTER_HH__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace Ase { 14 | 15 | /// Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible objects. 16 | template std::string string_format (const char *format, const Args &...args) __attribute__ ((__format__ (__printf__, 1, 0))); 17 | 18 | /// Class to push the POSIX/C locale_t (UTF-8) for the scope of its lifetime. 19 | class ScopedPosixLocale final { 20 | locale_t saved_locale_ = {}; 21 | void operator= (const ScopedPosixLocale&) = delete; 22 | ScopedPosixLocale (const ScopedPosixLocale&) = delete; 23 | public: 24 | explicit ScopedPosixLocale (); 25 | virtual ~ScopedPosixLocale (); 26 | static locale_t posix_locale(); 27 | }; 28 | 29 | // == Implementation Details == 30 | namespace Impl { 31 | using StringFormatVariant = std::variant; 32 | struct StringFormatArg : StringFormatVariant { 33 | using SL = std::list; // use list to keep c_str() references stable 34 | using StringFormatVariant::operator=; 35 | void assign (const char *s, SL &h) { *this = s; } 36 | void assign (const std::string &s, SL &h) { *this = s.c_str(); } 37 | void assign (std::nullptr_t, SL &h) { *this = uint64_t (0); } 38 | void assign (const void *p, SL &h) { *this = uint64_t (ptrdiff_t (p)); } 39 | template typename std::enable_if || std::is_enum_v, void> 40 | ::type assign (const T &v, SL &h) { *this = uint64_t (v); } 41 | template typename std::enable_if, void> 42 | ::type assign (const T &v, SL &h) { *this = double (v); } 43 | template typename std::enable_if::value, void> 44 | ::type assign (const T &o, SL &h) { std::ostringstream s; s << o; h.push_back (s.str()); *this = h.back().c_str(); } 45 | static std::string string_format_args (const char *format, size_t N, const StringFormatArg *args); 46 | }; 47 | } // Impl 48 | 49 | /** Format a string according to an sprintf() `format` string with `arguments`. 50 | * Refer to sprintf(3) for the format string details, this function is designed to 51 | * serve as an sprintf() replacement and mimick its behaviour as close as possible. 52 | * Supported format directive features are: 53 | * - Formatting flags (sign conversion, padding, alignment), i.e. the flags: [-#0+ '] 54 | * - Field width and precision specifications. 55 | * - Positional arguments for field width, precision and value. 56 | * - Length modifiers are tolerated: i.e. any of [hlLjztqZ]. 57 | * - The conversion specifiers [spmcCdiouXxFfGgEeAa]. 58 | * 59 | * @NOTE Format errors, e.g. missing arguments will produce a warning on stderr and 60 | * return the `format` string unmodified. 61 | * @returns A formatted string. 62 | */ 63 | template __attribute__ ((noinline)) std::string 64 | string_format (const char *format, const Args &...args) 65 | { 66 | constexpr size_t N = sizeof... (Args); 67 | Impl::StringFormatArg sfa[N ? N : 1]; 68 | size_t i = 0; 69 | Impl::StringFormatArg::SL templist; // keep temporary strings across string_format_args() 70 | (sfa[i++].assign (args, templist), ...); // C++17 fold expression 71 | return sfa[0].string_format_args (format, N, sfa); 72 | } 73 | 74 | } // Ase 75 | 76 | #endif // __ASE_FORMATTER_HH__ 77 | -------------------------------------------------------------------------------- /ase/gtk2wrap.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_GTK2WRAP_HH__ 3 | #define __ASE_GTK2WRAP_HH__ 4 | 5 | #include 6 | #include 7 | 8 | namespace Ase { 9 | 10 | struct Gtk2WindowSetup { 11 | std::string title; 12 | int width = 0, height = 0; 13 | std::function deleterequest_mt; 14 | }; 15 | 16 | struct Gtk2DlWrapEntry { 17 | ulong (*create_window) (const Gtk2WindowSetup &windowsetup); 18 | bool (*resize_window) (ulong windowid, int width, int height); 19 | void (*show_window) (ulong windowid); 20 | void (*hide_window) (ulong windowid); 21 | void (*destroy_window) (ulong windowid); 22 | void (*threads_enter) (); 23 | void (*threads_leave) (); 24 | }; 25 | 26 | } // Ase 27 | 28 | extern "C" { 29 | extern Ase::Gtk2DlWrapEntry Ase__Gtk2__wrapentry; 30 | } 31 | 32 | #endif // __ASE_GTK2WRAP_HH__ 33 | -------------------------------------------------------------------------------- /ase/inifile.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_INIFILE_HH__ 3 | #define __ASE_INIFILE_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | // == IniFile == 10 | /// Class to parse INI configuration file sections and values. 11 | class IniFile { 12 | typedef std::map SectionMap; 13 | SectionMap sections_; 14 | void load_ini (const String &inputname, const String &data); 15 | //bool set (const String §ion, const String &key, const String &value, const String &locale = ""); 16 | //bool del (const String §ion, const String &key, const String &locale = "*"); 17 | //bool value (const String &dotpath, const String &value); 18 | const StringS& section (const String &name) const; 19 | public: 20 | explicit IniFile (const String &name, const String &inidata); ///< Load INI file from immediate `data`. 21 | explicit IniFile (Blob blob); ///< Load INI file from Blob. 22 | explicit IniFile (const IniFile &source); ///< Copy constructor. 23 | IniFile& operator= (const IniFile &source); ///< Assignment operator. 24 | //String get (const String §ion, const String &key, const String &locale = "") const; 25 | bool has_sections () const; ///< Checks if IniFile is non-empty. 26 | StringS sections () const; ///< List all sections. 27 | bool has_section (const String §ion) const; ///< Check presence of a section. 28 | StringS attributes (const String §ion) const; ///< List all attributes available in `section`. 29 | bool has_attribute (const String §ion, const String &key) const; ///< Return if `section` contains `key`. 30 | bool has_raw_value (const String &dotpath, 31 | String *valuep = NULL) const; ///< Check and possibly retrieve raw value if present. 32 | String raw_value (const String &dotpath) const; ///< Retrieve raw (uncooked) value of section.attribute[locale]. 33 | StringS raw_values () const; ///< List all section.attribute=value pairs. 34 | String value_as_string (const String &dotpath) const; ///< Retrieve value of section.attribute[locale]. 35 | bool has_value (const String &dotpath, 36 | String *valuep = NULL) const; ///< Check and possibly retrieve value if present. 37 | static String cook_string (const String &input_string); ///< Unquote contents of `input_string` 38 | }; 39 | 40 | 41 | // == IniWriter == 42 | /// Class to write INI configuration file sections and values. 43 | class IniWriter { 44 | struct Section { 45 | String name; 46 | StringS entries; 47 | }; 48 | std::vector
sections_; 49 | Section* find_section (String name, bool create); 50 | size_t find_entry (Section §ion, String name, bool create); 51 | public: 52 | void set (String key, String value); 53 | String output (); 54 | }; 55 | 56 | } // Ase 57 | 58 | #endif // __ASE_INIFILE_HH__ 59 | -------------------------------------------------------------------------------- /ase/levenshtein.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_LEVENSHTEIN_HH__ 3 | #define __ASE_LEVENSHTEIN_HH__ 4 | 5 | #include "cxxaux.hh" 6 | 7 | namespace Ase { 8 | 9 | // Damerau-Levenshtein Distance with restricted transposition, memory requirement: 12 * max(|source|,|target|) + constant 10 | float damerau_levenshtein_restricted (const std::string &source, const std::string &target, 11 | const float ci = 1, // insertion 12 | const float cd = 1, // deletion 13 | const float cs = 1, // substitution 14 | const float ct = 1); // transposition 15 | 16 | // Damerau-Levenshtein Distance with unrestricted transpositions, memory requirement: 4 * |source|*|target| + constant 17 | float damerau_levenshtein_distance (const std::string &source, const std::string &target, 18 | const float ci = 1, // insertion 19 | const float cd = 1, // deletion 20 | const float cs = 1, // substitution 21 | const float ct = 1); // transposition 22 | 23 | } // Ase 24 | 25 | #endif // __ASE_LEVENSHTEIN_HH__ 26 | -------------------------------------------------------------------------------- /ase/liquidsfz.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | #include "../external/liquidsfz/lib/argparser.cc" 4 | #include "../external/liquidsfz/lib/hydrogenimport.cc" 5 | #include "../external/liquidsfz/lib/lfogen.cc" 6 | #include "../external/liquidsfz/lib/loader.cc" 7 | #include "../external/liquidsfz/lib/log.cc" 8 | #include "../external/liquidsfz/lib/midnam.cc" 9 | #include "../external/liquidsfz/lib/pugixml.cc" 10 | #include "../external/liquidsfz/lib/samplecache.cc" 11 | #include "../external/liquidsfz/lib/sfpool.cc" 12 | #include "../external/liquidsfz/lib/synth.cc" 13 | #include "../external/liquidsfz/lib/utils.cc" 14 | #include "../external/liquidsfz/lib/voice.cc" 15 | -------------------------------------------------------------------------------- /ase/liquidsfzapi.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | #include "../external/liquidsfz/lib/liquidsfz.cc" 4 | -------------------------------------------------------------------------------- /ase/logging.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "logging.hh" 3 | #include "platform.hh" 4 | #include "path.hh" 5 | #include "regex.hh" 6 | #include 7 | #include 8 | 9 | namespace Ase { 10 | 11 | LogFlags log_setup (int*) __attribute__ ((__weak__)); 12 | 13 | uint64_t 14 | timestamp_now () 15 | { 16 | struct timeval tv = { 0, 0 }; 17 | gettimeofday (&tv, nullptr); 18 | return tv.tv_sec * 1000000ULL + tv.tv_usec; 19 | } 20 | 21 | static uint64 programstart_timestamp = timestamp_now(); 22 | static uint32_t log_flags = 0; 23 | static int log_fd = -1; 24 | static bool log_colorize = true; 25 | 26 | static void 27 | logstart() 28 | { 29 | if (log_flags) [[likely]] return; 30 | log_colorize = AnsiColors::colorize_tty(); 31 | if (log_setup) { 32 | log_flags = log_setup (&log_fd); 33 | if (log_fd >= 0) 34 | log_flags |= LOG_FILE; 35 | } else 36 | log_flags |= LOG_STDERR; 37 | const time_t now = programstart_timestamp / 1000000; 38 | struct tm stm{}; 39 | localtime_r (&now, &stm); 40 | char tbuf[128] = { 0, }; 41 | strftime (tbuf, sizeof (tbuf) - 1, "%Y-%m-%d %H:%M:%S %z", &stm); 42 | const std::string exec = executable_path(); 43 | const char *bexec = strrchr (exec.c_str(), '/'); 44 | bexec = bexec ? bexec+1 : exec.c_str(); 45 | char pidbuf[64] = { 0, }; 46 | snprintf (pidbuf, sizeof (pidbuf) - 1, " pid=%u", getpid()); 47 | std::string msg = std::string (bexec) + ": programstart=\"" + tbuf + "\"" + pidbuf + " executable=\"" + executable_path() + "\""; 48 | logmsg (msg, "", 0, ""); 49 | } 50 | 51 | void 52 | logmsg (const std::string &msg, const char *const filename, const uint64_t columnline, const char *const function_name) 53 | { 54 | const uint32_t line = uint32_t (columnline), column = columnline >> 32; 55 | logstart(); 56 | ScopedPosixLocale posix_locale; // use POSIX locale for this scope 57 | if (msg.empty()) return; 58 | String s = msg; 59 | if (s[s.size()-1] != '\n') 60 | s += "\n"; 61 | { 62 | char tstamp[64] = { 0, }; 63 | snprintf (tstamp, sizeof (tstamp) - 1, "%.6f: ", 0.000001 * (timestamp_now() - programstart_timestamp)); 64 | s = tstamp + s; 65 | } 66 | if (filename && filename[0] && function_name) { 67 | char linein[128] = { 0, }; 68 | snprintf (linein, sizeof (linein) - 1, ":%u:%u: execution at: ", line, column); 69 | s = filename + std::string (linein) + function_name + ":\n" + s; 70 | } 71 | if (log_fd == 2 || log_flags & LOG_STDERR) 72 | fflush (stderr); 73 | if (!log_colorize && log_flags & LOG_STDERR) 74 | write (2, s.data(), s.size()); 75 | if (log_fd >= 0) 76 | write (log_fd, s.data(), s.size()); 77 | if (log_colorize && log_flags & LOG_STDERR) { 78 | using namespace AnsiColors; 79 | const auto C = color (FG_CYAN), G = color (BOLD, FG_GREEN), B = color (FG_BLUE), Y = color (FG_YELLOW), R = color (RESET); 80 | const std::string HEXINT = "0[xX][0-9abcdefABCDEF]+"; 81 | const std::string FULLFLOAT = "([1-9][0-9]*|0)([.][0-9]*)?([eE][+-]?[0-9]+)?"; 82 | const std::string FRACTFLOAT = "[.][0-9]+([eE][+-]?[0-9]+)?"; 83 | const std::string NUMBER = HEXINT + "|" + FULLFLOAT + "|" + FRACTFLOAT; 84 | s = Re::sub ("=(" + NUMBER + ")", "=" + Y + "$1" + R, s); 85 | s = Re::sub ("=(\"(?:[^\"\\\\]|\\\\.)*\")", "=" + B + "$1" + R, s); 86 | s = Re::sub (" (\\w+)=", " " + C + "$1" + R + "=", s); 87 | s = Re::sub (": ([a-zA-Z.0-9_:-]+): ", ": " + G + "$1:" + R + " ", s); 88 | s = Re::sub ("^(\\d+[.]\\d+):", Y + "$1:" + R, s, Re::M); 89 | write (2, s.data(), s.size()); 90 | } 91 | } 92 | 93 | } // Ase 94 | -------------------------------------------------------------------------------- /ase/logging.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace Ase { 8 | 9 | /// Wrap a string together with its source code location 10 | struct LogFormat { 11 | std::source_location location; 12 | const char *const cstr = nullptr; 13 | LogFormat (const char *s, std::source_location l = std::source_location::current()) : 14 | location (l), 15 | cstr (s) 16 | {} 17 | }; 18 | 19 | /// Current time in µseconds. 20 | uint64_t timestamp_now (); 21 | 22 | /// Write a log message to the log file (or possibly stderr), using the POSIX/C locale. 23 | template void log (const LogFormat &format, const A &...args); 24 | 25 | /// Flags to configure logging behaviour 26 | enum LogFlags { LOG_FILE = 1, LOG_STDERR = 2, LOG_LOCATIONS = 4, }; 27 | 28 | /// Configurable handler to open log files 29 | LogFlags log_setup (int *logfd); 30 | 31 | // Keep natural logarithmic function available 32 | #ifdef _MATH_H 33 | using ::log; 34 | #endif 35 | 36 | // == implementations == 37 | void logmsg (const std::string &msg, const char *file, uint64_t columnline, const char *func); 38 | 39 | template __attribute__ ((__noinline__)) void 40 | logfmt (const char *file, uint64_t columnline, const char *func, const char *format, const A &...args) 41 | { 42 | logmsg (string_format (format, args...), file, columnline, func); 43 | } 44 | 45 | template __attribute__ ((__always_inline__)) inline void 46 | log (const LogFormat &format, const A &...args) 47 | { 48 | logfmt (format.location.file_name(), 49 | uint64_t (format.location.column()) << 32 | uint32_t (format.location.line()), 50 | format.location.function_name(), 51 | format.cstr, args...); 52 | } 53 | 54 | } // Ase 55 | -------------------------------------------------------------------------------- /ase/main.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_MAIN_HH__ 3 | #define __ASE_MAIN_HH__ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace Ase { 13 | 14 | // == MainApp == 15 | struct MainApp { 16 | AudioEngine *engine = nullptr; 17 | String pcm_override, midi_override; 18 | WebSocketServer *web_socket_server = nullptr; 19 | const char *outputfile = nullptr; 20 | std::vector args; 21 | uint16 websocket_port = 0; 22 | int jsonapi_logflags = 1; 23 | bool norc = true; 24 | bool allow_randomization = true; 25 | bool list_drivers = false; 26 | bool play_autostart = false; 27 | double play_autostop = D64MAX; 28 | enum ModeT { SYNTHENGINE, CHECK_INTEGRITY_TESTS }; 29 | ModeT mode = SYNTHENGINE; 30 | }; 31 | extern const MainApp &App; 32 | 33 | // == Jobs & main loop == 34 | extern MainLoopP main_loop; 35 | void main_loop_wakeup (); 36 | void main_loop_autostop_mt (); 37 | 38 | /// Execute a job callback in the Ase main loop. 39 | extern JobQueue main_jobs; 40 | 41 | /// Add a simple callback to the main event loop, without using malloc (obstruction free). 42 | struct RtJobQueue { void operator+= (const RtCall&); }; 43 | /// Queue a callback for the `main_loop` without invoking malloc(), addition is obstruction free. 44 | extern RtJobQueue main_rt_jobs; 45 | 46 | } // Ase 47 | 48 | #endif // __ASE_MAIN_HH__ 49 | -------------------------------------------------------------------------------- /ase/mathutils.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "mathutils.hh" 3 | #include "internal.hh" 4 | 5 | namespace Ase { 6 | 7 | } // Ase 8 | 9 | #include "testing.hh" 10 | 11 | namespace { // Anon 12 | using namespace Ase; 13 | 14 | TEST_INTEGRITY (mathutils_tests); 15 | static void 16 | mathutils_tests() 17 | { 18 | float f; 19 | double d; 20 | // check proper Inf and NaN handling (depends on compiler flags) 21 | f = d = .5 * INFINITY; TASSERT (d > 0 && f > 0 && std::isinf (f) && std::isinf (d)); 22 | f = d = -3 * INFINITY; TASSERT (d < 0 && f < 0 && std::isinf (f) && std::isinf (d)); 23 | f = f - f; TASSERT (!(f == f) && std::isnan (f)); // Infinity - Infinity yields NaN 24 | d = 0.0 / 0.0; TASSERT (!(d == d) && std::isnan (d)); 25 | // check rounding 26 | TASSERT (int (+0.40) == +0.0 && irintf (+0.40) == +0.0); 27 | TASSERT (int (-0.40) == -0.0 && irintf (-0.40) == -0.0); 28 | TASSERT (int (+0.51) == +0.0 && irintf (+0.51) == +1.0); 29 | TASSERT (int (-0.51) == -0.0 && irintf (-0.51) == -1.0); 30 | TASSERT (int (+1.90) == +1.0 && irintf (+1.90) == +2.0); 31 | TASSERT (int (-1.90) == -1.0 && irintf (-1.90) == -2.0); 32 | // check fast_exp2(int), 2^(-126..+127) must be calculated with 0 error 33 | volatile float mf = 1.0, pf = 1.0; 34 | for (ssize_t i = 0; i <= 127; i++) 35 | { 36 | // printerr ("2^±%-3d = %15.10g, %-.14g\n", i, fast_exp2 (i), fast_exp2 (-i)); 37 | TASSERT (fast_exp2 (i) == pf); 38 | if (i != 127) 39 | TASSERT (fast_exp2 (-i) == mf); 40 | else 41 | TASSERT (fast_exp2 (-i) <= mf); // single precision result for 2^-127 is 0 or subnormal 42 | pf *= 2; 43 | mf /= 2; 44 | } 45 | // check fast_exp2 error margin in [-1..+1] 46 | const double exp2step = Test::slow() ? 0.0000001 : 0.0001; 47 | for (d = -1; d <= +1; d += exp2step) 48 | { 49 | const double r = std::exp2 (d); 50 | const double err = std::fabs (r - fast_exp2 (d)); 51 | TASSERT (err < 4e-7); 52 | } 53 | // check fast_log2(int), log2(2^(1..127)) must be calculated with 0 error 54 | pf = 1.0; 55 | for (ssize_t i = 0; i <= 127; i++) 56 | { 57 | if (fast_log2 (pf) != float (i)) 58 | printerr ("fast_log2(%.17g)=%.17g\n", pf, fast_log2 (pf)); 59 | TASSERT (fast_log2 (pf) == float (i)); 60 | pf *= 2; 61 | } 62 | TASSERT (-126 == fast_log2 (1.1754944e-38)); 63 | TASSERT ( -64 == fast_log2 (5.421011e-20)); 64 | TASSERT ( -16 == fast_log2 (1.525879e-5)); 65 | TASSERT ( -8 == fast_log2 (0.00390625)); 66 | TASSERT ( -4 == fast_log2 (0.0625)); 67 | TASSERT ( -2 == fast_log2 (0.25)); 68 | TASSERT ( -1 == fast_log2 (0.5)); 69 | TASSERT ( 0 == fast_log2 (1)); 70 | // check fast_log2 error margin in [1/16..16] 71 | const double log2step = Test::slow() ? 0.0000001 : 0.0001; 72 | for (double d = 1 / 16.; d <= 16; d += log2step) 73 | { 74 | const double r = std::log2 (d); 75 | const double err = std::fabs (r - fast_log2 (d)); 76 | if (!(err < 3.8e-6)) 77 | printerr ("fast_log2(%.17g)=%.17g (diff=%.17g)\n", d, fast_log2 (d), r - fast_log2 (d)); 78 | TASSERT (err < 3.8e-6); 79 | } 80 | } 81 | 82 | } // Anon 83 | -------------------------------------------------------------------------------- /ase/midilib.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_MIDILIB_HH__ 3 | #define __ASE_MIDILIB_HH__ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Ase { 10 | 11 | namespace MidiLib { 12 | 13 | /// Aggregation of MIDI events and sequencing information. 14 | struct MidiFeed { 15 | ClipImplGeneratorS generators; 16 | TrackImpl::ClipScout scout; 17 | int trigger = ~0; 18 | }; 19 | using MidiFeedP = std::shared_ptr; 20 | 21 | class MidiProducerIface : public AudioProcessor { 22 | public: 23 | struct Position { 24 | int next = -1; 25 | int current = -1; 26 | double tick = -1; 27 | }; 28 | virtual void update_feed (MidiFeedP &feed) = 0; 29 | virtual Position* position () const = 0; // MT-Safe 30 | virtual void start () = 0; 31 | virtual void stop (bool restart = false) = 0; 32 | MidiProducerIface (const ProcessorSetup &psetup) : AudioProcessor (psetup) {} 33 | }; 34 | 35 | using MidiProducerIfaceP = std::shared_ptr; 36 | 37 | } // MidiLib 38 | } // Ase 39 | 40 | #endif // __ASE_MIDILIB_HH__ 41 | 42 | -------------------------------------------------------------------------------- /ase/mime-types.hh: -------------------------------------------------------------------------------- 1 | // Dedicated to the Public Domain under the Unlicense: https://unlicense.org/UNLICENSE 2 | 3 | // Source: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types 4 | static const char mime_types[] = R""""( 5 | application/gzip gz 6 | application/javascript js cjs mjs 7 | application/json json 8 | application/ogg ogx 9 | application/pdf pdf 10 | application/postscript ai eps eps2 eps3 epsf epsi ps 11 | application/wasm wasm 12 | application/x-bittorrent torrent 13 | application/x-debian-package deb udeb 14 | application/x-font gsf pfa pfb 15 | application/x-gtar gtar 16 | application/x-gtar-compressed taz tgz 17 | application/x-iso9660-image iso 18 | application/x-redhat-package-manager rpm 19 | application/x-rss+xml rss 20 | application/x-tar tar 21 | application/x-xz xz 22 | application/xhtml+xml xht xhtml 23 | application/xml xml xsd 24 | application/zip zip 25 | audio/adpcm adp 26 | audio/amr amr 27 | audio/amr-wb awb 28 | audio/annodex axa 29 | audio/basic au snd 30 | audio/midi kar mid midi rmi 31 | audio/mp4 m4a mp4a 32 | audio/mpeg m2a m3a mp2 mp2a mp3 mpega mpga 33 | audio/ogg oga ogg opus spx 34 | audio/prs.sid sid 35 | audio/s3m s3m 36 | audio/silk sil 37 | audio/webm weba 38 | audio/x-aac aac 39 | audio/x-aiff aif aifc aiff 40 | audio/x-caf caf 41 | audio/x-csound csd orc sco 42 | audio/x-flac flac 43 | audio/x-gsm gsm 44 | audio/x-matroska mka 45 | audio/x-mpegurl m3u 46 | audio/x-ms-wax wax 47 | audio/x-ms-wma wma 48 | audio/x-pn-realaudio ra ram rm 49 | audio/x-scpls pls 50 | audio/x-sd2 sd2 51 | audio/x-wav wav 52 | audio/xm xm 53 | font/collection ttc 54 | font/otf otf 55 | font/ttf ttf 56 | font/woff woff 57 | font/woff2 woff2 58 | image/gif gif 59 | image/jp2 jp2 jpg2 60 | image/jpeg jpe jpeg jpg 61 | image/jpx jpf jpx 62 | image/png png 63 | image/svg+xml svg svgz 64 | image/tiff tif tiff 65 | image/webp webp 66 | image/x-icon ico 67 | message/rfc822 eml mime 68 | text/css css 69 | text/csv csv 70 | text/html htm html shtml 71 | text/markdown markdown md 72 | text/mathml mml 73 | text/plain asc log pot text txt 74 | text/richtext rtx 75 | text/tab-separated-values tsv 76 | text/troff man me ms roff t tr 77 | text/x-c c c++ cc cpp cxx h h++ hh hpp hxx 78 | text/x-diff diff patch 79 | text/x-sass sass 80 | text/x-scss scss 81 | video/mp4 mp4 82 | video/ogg ogv 83 | video/webm webm 84 | video/x-matroska mkv 85 | )""""; 86 | -------------------------------------------------------------------------------- /ase/minizip.c: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #define HAVE_ZLIB 3 | #include "minizip.h" 4 | 5 | #include "external/minizip-ng/mz_zip.c" 6 | #include "external/minizip-ng/mz_strm.c" 7 | #include "external/minizip-ng/mz_crypt.c" // mz_crypt_crc32_update 8 | #include "external/minizip-ng/mz_zip_rw.c" 9 | #include "external/minizip-ng/mz_strm_buf.c" 10 | #include "external/minizip-ng/mz_strm_mem.c" 11 | #include "external/minizip-ng/mz_strm_zlib.c" 12 | #include "external/minizip-ng/mz_strm_split.c" 13 | #include "external/minizip-ng/mz_os.c" 14 | #include "external/minizip-ng/mz_os_posix.c" 15 | #include "external/minizip-ng/mz_strm_os_posix.c" 16 | //#include "external/minizip-ng/mz_compat.c" 17 | -------------------------------------------------------------------------------- /ase/minizip.h: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_MINIZIP_H__ 3 | #define __ASE_MINIZIP_H__ 4 | 5 | #include 6 | 7 | // Configuration 8 | #define MZ_ZIP_NO_ENCRYPTION 9 | 10 | // Minizip API 11 | #include "external/minizip-ng/mz.h" 12 | #include "external/minizip-ng/mz_os.h" 13 | #include "external/minizip-ng/mz_zip.h" 14 | #include "external/minizip-ng/mz_strm.h" 15 | //#include "external/minizip-ng/mz_crypt.h" 16 | #include "external/minizip-ng/mz_strm_buf.h" 17 | #include "external/minizip-ng/mz_strm_mem.h" 18 | #include "external/minizip-ng/mz_strm_zlib.h" 19 | #include "external/minizip-ng/mz_strm_split.h" 20 | #include "external/minizip-ng/mz_strm_os.h" 21 | #include "external/minizip-ng/mz_zip_rw.h" 22 | //#include "external/minizip-ng/mz_compat.h" 23 | 24 | #endif // __ASE_MINIZIP_H__ 25 | -------------------------------------------------------------------------------- /ase/monitor.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "monitor.hh" 3 | #include "track.hh" 4 | #include "jsonipc/jsonipc.hh" 5 | #include "internal.hh" 6 | 7 | namespace Ase { 8 | 9 | // == MonitorImpl == 10 | MonitorImpl::MonitorImpl() 11 | {} 12 | 13 | MonitorImpl::~MonitorImpl() 14 | {} 15 | 16 | DeviceP 17 | MonitorImpl::get_output () // TODO: implement 18 | { 19 | return nullptr; 20 | } 21 | 22 | int32 23 | MonitorImpl::get_ochannel () // TODO: implement 24 | { 25 | return -1; 26 | } 27 | 28 | int64 29 | MonitorImpl::get_mix_freq () // TODO: implement 30 | { 31 | return 0; 32 | } 33 | 34 | int64 35 | MonitorImpl::get_frame_duration () // TODO: implement 36 | { 37 | return 0; 38 | } 39 | 40 | } // Ase 41 | -------------------------------------------------------------------------------- /ase/monitor.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_MONITOR_HH__ 3 | #define __ASE_MONITOR_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | class MonitorImpl : public GadgetImpl, public virtual Monitor { 10 | ASE_DEFINE_MAKE_SHARED (MonitorImpl); 11 | friend class TrackImpl; 12 | virtual ~MonitorImpl (); 13 | public: 14 | explicit MonitorImpl (); 15 | DeviceP get_output () override; 16 | int32 get_ochannel () override; 17 | int64 get_mix_freq () override; 18 | int64 get_frame_duration () override; 19 | }; 20 | using MonitorImplP = std::shared_ptr; 21 | 22 | } // Ase 23 | 24 | #endif // __ASE_MONITOR_HH__ 25 | -------------------------------------------------------------------------------- /ase/nativedevice.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_NATIVE_DEVICE_HH__ 3 | #define __ASE_NATIVE_DEVICE_HH__ 4 | 5 | #include 6 | #include 7 | 8 | namespace Ase { 9 | 10 | class NativeDeviceImpl : public DeviceImpl, public virtual NativeDevice { 11 | AudioProcessorP const proc_; 12 | AudioComboP combo_; // maybe null 13 | DeviceS children_; 14 | DeviceInfo const info_; 15 | ASE_DEFINE_MAKE_SHARED (NativeDeviceImpl); 16 | using DeviceFunc = std::function; 17 | DeviceP insert_device (const String &uri, Device *sibling, const DeviceFunc &loader); 18 | protected: 19 | static DeviceP create_native_device (AudioEngine &engine, const String ®istryuri); 20 | void serialize (WritNode &xs) override; 21 | void _set_parent (GadgetImpl *parent) override; 22 | void _activate () override; 23 | void _deactivate () override; 24 | explicit NativeDeviceImpl (const String &aseid, AudioProcessor::StaticInfo, AudioProcessorP); 25 | public: 26 | PropertyS access_properties () override; 27 | AudioProcessorP _audio_processor () const override { return proc_; } 28 | AudioComboP audio_combo () const { return combo_; } 29 | bool is_combo_device () override { return combo_ != nullptr; } 30 | DeviceInfo device_info () override { return info_; } 31 | // handle sub Devices 32 | DeviceS list_devices () override { return children_; } 33 | void remove_device (Device &sub) override; 34 | DeviceP append_device (const String &uri) override; 35 | DeviceP insert_device (const String &uri, Device &beforesibling) override; 36 | void remove_all_devices (); 37 | void _set_event_source (AudioProcessorP esource) override; 38 | void _disconnect_remove () override; 39 | }; 40 | 41 | DeviceP create_processor_device (AudioEngine &engine, const String &uri, bool engineproducer); 42 | 43 | } // Ase 44 | 45 | #endif // __ASE_NATIVE_DEVICE_HH__ 46 | -------------------------------------------------------------------------------- /ase/object.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_OBJECT_HH__ 3 | #define __ASE_OBJECT_HH__ 4 | 5 | #include 6 | #include 7 | 8 | namespace Ase { 9 | 10 | /// Implementation type for classes with Event subscription. 11 | class EmittableImpl : public virtual Emittable { 12 | struct EventDispatcher; 13 | EventDispatcher *ed_ = nullptr; 14 | friend class EventConnection; 15 | protected: 16 | virtual ~EmittableImpl (); 17 | public: 18 | ASE_USE_RESULT 19 | Connection on_event (const String &eventselector, const EventHandler &eventhandler) override; 20 | void emit_event (const String &type, const String &detail, const ValueR fields = {}) override; 21 | void emit_notify (const String &detail) override; 22 | }; 23 | 24 | class CoalesceNotifies { 25 | struct Notification { 26 | EmittableP emittable; String detail; 27 | bool operator== (const Notification &b) const { return emittable == b.emittable && detail == b.detail; } 28 | }; 29 | struct NotificationHash { size_t operator() (const Notification&) const; }; 30 | using NotificationSet = std::unordered_set; 31 | NotificationSet notifications_; 32 | CoalesceNotifies *next_ = nullptr; 33 | public: 34 | explicit CoalesceNotifies (); 35 | void flush_notifications (); 36 | /*dtor*/ ~CoalesceNotifies (); 37 | friend class EmittableImpl; 38 | }; 39 | 40 | /// Implementation type for classes with Property interfaces. 41 | class ObjectImpl : public EmittableImpl, public virtual Object { 42 | protected: 43 | virtual ~ObjectImpl () = 0; 44 | public: 45 | }; 46 | using ObjectImplP = std::shared_ptr; 47 | 48 | } // Ase 49 | 50 | #endif // __ASE_OBJECT_HH__ 51 | -------------------------------------------------------------------------------- /ase/queuemux.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "queuemux.hh" 3 | #include "randomhash.hh" 4 | #include "internal.hh" 5 | #include "testing.hh" 6 | 7 | #define QDEBUG(...) Ase::debug ("queuemux", __VA_ARGS__) 8 | 9 | namespace { 10 | using namespace Ase; 11 | 12 | struct SomeValue { 13 | int i; 14 | }; 15 | 16 | static __attribute__ ((always_inline)) inline long 17 | QueueMultiplexer_priority (const SomeValue &o) 18 | { 19 | return o.i; 20 | } 21 | 22 | TEST_INTEGRITY (queuemux_test); 23 | static void 24 | queuemux_test() 25 | { 26 | // generate ascending (sorted) sample values 27 | int ascending_counter = -17; 28 | auto ascending_values = [&ascending_counter] () { 29 | if (random_int64() & 1) 30 | ascending_counter++; 31 | return ascending_counter; 32 | }; 33 | const size_t TOTAL = 39; 34 | std::vector samples; 35 | for (size_t i = 0; i < TOTAL; i++) 36 | samples.push_back (ascending_values()); 37 | 38 | // setting: N queues contain ascending (sorted) values 39 | constexpr size_t N = 4; 40 | using Queue = std::vector; 41 | std::vector queues; 42 | queues.resize (N); 43 | for (int v : samples) 44 | queues[random_int64() % N].push_back (SomeValue { v }); 45 | 46 | // task: fetch values from all queues in sorted order 47 | std::array queue_ptrs{}; 48 | for (size_t i = 0; i < queues.size(); i++) 49 | queue_ptrs[i] = &queues[i]; 50 | QueueMultiplexer mux; 51 | mux.assign (queue_ptrs); 52 | TASSERT (mux.count_pending() == TOTAL); 53 | int last = -2147483648; 54 | size_t sc = 0; 55 | while (mux.more()) 56 | { 57 | const SomeValue ¤t = mux.pop(); 58 | QDEBUG ("QueueMultiplexer: %d\n", current.i); 59 | TASSERT (current.i >= last); 60 | last = current.i; 61 | TASSERT (sc < samples.size() && samples[sc++] == current.i); 62 | } 63 | TASSERT (sc == samples.size()); 64 | TASSERT (mux.count_pending() == 0); 65 | } 66 | 67 | } // Anon 68 | -------------------------------------------------------------------------------- /ase/regex.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_REGEX_HH__ 3 | #define __ASE_REGEX_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | /// Wrapper for std::regex to simplify usage and reduce compilation time 10 | class Re final { 11 | public: 12 | enum Flags : int32_t { 13 | DEFAULT = 0, 14 | ERE = 1 << 0, // Posix Extended 15 | I = 1 << 4, // IGNORECASE 16 | M = 1 << 5, // MULTILINE 17 | N = 1 << 6, // NO_AUTO_CAPTURE 18 | S = 1 << 7, // DOTALL 19 | X = 1 << 8, // EXTENDED 20 | XX = 1 << 9, // EXTENDED_MORE 21 | J = 1 << 10, // DUPNAMES 22 | U = 1 << 11, // UNGREEDY 23 | }; 24 | static StringS findall (const String ®ex, const String &input, Flags = DEFAULT); 25 | static ssize_t search (const String ®ex, const String &input, Flags = DEFAULT); 26 | static String grep (const String ®ex, const String &input, int group = 0, Flags = DEFAULT); 27 | static String sub (const String ®ex, const String &subst, const String &input, Flags = DEFAULT); 28 | static String sub (const String ®ex, const String &subst, const String &input, uint count, Flags = DEFAULT); 29 | }; 30 | extern constexpr inline Re::Flags operator| (Re::Flags a, Re::Flags b) { return Re::Flags (int32_t (a) | int32_t (b)); } 31 | 32 | } // Ase 33 | 34 | #endif // __ASE_REGEX_HH__ 35 | -------------------------------------------------------------------------------- /ase/server.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_SERVER_HH__ 3 | #define __ASE_SERVER_HH__ 4 | 5 | #include 6 | #include 7 | 8 | namespace Ase { 9 | 10 | class ServerImpl : public GadgetImpl, public virtual Server { 11 | FastMemory::Arena telemetry_arena; 12 | public: 13 | static ServerImplP instancep (); 14 | explicit ServerImpl (); 15 | virtual ~ServerImpl (); 16 | bool set_data (const String &key, const Value &v) override; 17 | Value get_data (const String &key) const override; 18 | String get_version () override; 19 | String get_build_id () override; 20 | String get_opus_version () override; 21 | String get_flac_version () override; 22 | String get_clap_version () override; 23 | String error_blurb (Error error) const override; 24 | String musical_tuning_label (MusicalTuning musicaltuning) const override; 25 | String musical_tuning_blurb (MusicalTuning musicaltuning) const override; 26 | uint64 user_note (const String &text, const String &channel = "misc", UserNote::Flags flags = UserNote::TRANSIENT, const String &rest = "") override; 27 | bool user_reply (uint64 noteid, uint r) override; 28 | bool broadcast_telemetry (const TelemetrySegmentS &plan, int32 interval_ms) override; 29 | void shutdown () override; 30 | ProjectP last_project () override; 31 | ProjectP create_project (String projectname) override; 32 | PropertyP access_preference (const String &ident) override; 33 | StringS list_preferences () override; 34 | using Block = FastMemory::Block; 35 | Block telemem_allocate (uint32 length) const; 36 | void telemem_release (Block telememblock) const; 37 | ptrdiff_t telemem_start () const; 38 | }; 39 | extern ServerImpl *SERVER; 40 | 41 | // static constexpr const char* telemetry_type (const int64 &field) { return "i64"; } 42 | static constexpr const char* telemetry_type (const int8 &field) { return "i8"; } 43 | static constexpr const char* telemetry_type (const int32 &field) { return "i32"; } 44 | static constexpr const char* telemetry_type (const float &field) { return "f32"; } 45 | static constexpr const char* telemetry_type (const double &field) { return "f64"; } 46 | 47 | template inline TelemetryField 48 | telemetry_field (const String &name, const T *field) 49 | { 50 | auto start = ServerImpl::instancep()->telemem_start(); 51 | const ptrdiff_t offset = ptrdiff_t (field) - start; 52 | ASE_ASSERT_RETURN (offset >= 0 && offset < 2147483647, {}); // INT_MAX 53 | TelemetryField tfield { name, telemetry_type (*field), int32 (offset), sizeof (*field) }; 54 | return tfield; 55 | } 56 | 57 | } // Ase 58 | 59 | #endif // __ASE_SERVER_HH__ 60 | -------------------------------------------------------------------------------- /ase/signalmath.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "signalmath.hh" 3 | #include "internal.hh" 4 | 5 | namespace Ase { 6 | 7 | } // Ase 8 | 9 | #include "testing.hh" 10 | 11 | namespace { // Anon 12 | using namespace Ase; 13 | 14 | #define FEQUAL(a,b,eps) do { \ 15 | auto __a = a; auto __b = b; \ 16 | if (std::abs (__a - __b) <= eps) \ 17 | break; \ 18 | std::string __m = \ 19 | Ase::string_format ("'%s ≈ %s': %+g ≈ %+g (Δ=%g)", \ 20 | ASE_CPP_STRINGIFY (a), \ 21 | ASE_CPP_STRINGIFY (b), \ 22 | __a, __b, std::abs (__a - __b)); \ 23 | Ase::assertion_failed (__m.c_str()); \ 24 | } while (0) 25 | 26 | TEST_INTEGRITY (signalutils_tests); 27 | static void 28 | signalutils_tests() 29 | { 30 | float f; 31 | double d; 32 | // check Voltage <-> Hertz 33 | constexpr auto fe = 0.005, ve = 0.000001; 34 | float w; 35 | w = -0.3; f = +32.703; FEQUAL (f, voltage2hz (w), fe); FEQUAL (w, hz2voltage (f), ve); 36 | ; FEQUAL (f, fast_voltage2hz (w), fe); FEQUAL (w, fast_hz2voltage (f), ve); 37 | w = +0.0; f = +261.625; FEQUAL (f, voltage2hz (w), fe); FEQUAL (w, hz2voltage (f), ve); 38 | ; FEQUAL (f, fast_voltage2hz (w), fe); FEQUAL (w, fast_hz2voltage (f), ve); 39 | w = +0.5; f = +8372.018; FEQUAL (f, voltage2hz (w), fe); FEQUAL (w, hz2voltage (f), ve); 40 | ; FEQUAL (f, fast_voltage2hz (w), fe); FEQUAL (w, fast_hz2voltage (f), ve); 41 | w = +0.6; f = +16744.036; FEQUAL (f, voltage2hz (w), fe); FEQUAL (w, hz2voltage (f), ve); 42 | ; FEQUAL (f, fast_voltage2hz (w), fe); FEQUAL (w, fast_hz2voltage (f), ve); 43 | // check Voltage <-> dB 44 | constexpr auto de = 0.01; 45 | d = -6.0206; w = 0.5; FEQUAL (d, voltage2db (w), de); FEQUAL (w, db2voltage (d), ve); 46 | ; FEQUAL (d, fast_voltage2db (w), de); FEQUAL (w, fast_db2voltage (d), ve); 47 | d = +0.0; w = 1.0; FEQUAL (d, voltage2db (w), de); FEQUAL (w, db2voltage (d), ve); 48 | ; FEQUAL (d, fast_voltage2db (w), de); FEQUAL (w, fast_db2voltage (d), ve); 49 | d = +7.9588; w = 2.5; FEQUAL (d, voltage2db (w), de); FEQUAL (w, db2voltage (d), ve); 50 | ; FEQUAL (d, fast_voltage2db (w), de); FEQUAL (w, fast_db2voltage (d), ve); 51 | } 52 | 53 | } // Anon 54 | -------------------------------------------------------------------------------- /ase/sndfile.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "testing.hh" 3 | #include "internal.hh" 4 | 5 | #include "external/libsndfile/include/sndfile.hh" 6 | 7 | #define SDEBUG(...) Ase::debug ("sndfile", __VA_ARGS__) 8 | 9 | // Check libsndfile-1.1.0 header features 10 | static_assert (SF_FORMAT_MPEG >= 0x230000, "libsndfile required with MP3 support"); 11 | 12 | namespace Ase { 13 | 14 | } // Ase 15 | 16 | // == tests == 17 | namespace { 18 | using namespace Ase; 19 | 20 | TEST_INTEGRITY (sndfile_tests); 21 | static void 22 | sndfile_tests() 23 | { 24 | char sndfileversion[256] = { 0, }; 25 | sf_command (nullptr, SFC_GET_LIB_VERSION, sndfileversion, sizeof (sndfileversion)); 26 | SDEBUG ("SFC_GET_LIB_VERSION: %s\n", sndfileversion); 27 | } 28 | 29 | } // Anon 30 | 31 | // Check libsndfile configuration in local build 32 | #include "sndfile/src/config.h" 33 | static_assert (HAVE_EXTERNAL_XIPH_LIBS, "libsndfile requires Ogg/Vorbis and Opus"); 34 | static_assert (HAVE_MPEG, "libsndfile requires libmpg123 and libmp3lame"); 35 | -------------------------------------------------------------------------------- /ase/sortnet.cc: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #include "sortnet.hh" 3 | #include "defs.hh" 4 | #include "internal.hh" 5 | 6 | // == Testing == 7 | #include "testing.hh" 8 | 9 | namespace { // Anon 10 | using namespace Ase; 11 | 12 | template void 13 | check_permutations () 14 | { 15 | std::array permarray; 16 | for (size_t i = 0; i < N; i++) 17 | permarray[i] = i; 18 | do 19 | { 20 | std::array array { permarray }; 21 | fixed_sort (&array[0], &array[N], std::less()); 22 | for (size_t i = 1; i < N; i++) 23 | assert_return (array[i - 1] <= array[i]); 24 | } 25 | while (std::next_permutation (&permarray[0], &permarray[N])); 26 | } 27 | 28 | static uint64_t romu_state64a = 1, romu_state64b = 0xda942042e4dd58b5ULL; 29 | static inline uint64_t 30 | romumono2() 31 | { 32 | const uint64_t result1 = romu_state64a, result2 = romu_state64b; 33 | romu_state64a = rotl (romu_state64a, 32) * 15241094284759029579u; 34 | romu_state64b = rotl (romu_state64b, 32) * 0x5851f42d4c957f2dUL; 35 | return result1 | (uint64_t (result2) << 32); 36 | } 37 | 38 | template void 39 | check_randomized (size_t runs) 40 | { 41 | std::array array; 42 | for (size_t j = 0; j < runs; j++) 43 | { 44 | for (size_t i = 0; i < N; i++) 45 | array[i] = romumono2(); 46 | fixed_sort (&array[0], &array[N], std::less()); 47 | for (size_t i = 1; i < N; i++) 48 | assert_return (array[i - 1] <= array[i]); 49 | } 50 | } 51 | 52 | TEST_INTEGRITY (sortnet_tests); 53 | 54 | static void 55 | sortnet_tests() 56 | { 57 | { // setup PRNG 58 | romu_state64a = Test::random_int64() | 1; 59 | romu_state64b = Test::random_int64(); 60 | romumono2(); romumono2(); romumono2(); romumono2(); romumono2(); 61 | } 62 | constexpr const size_t RUNS = 9999; 63 | check_permutations<1,int>(); 64 | check_permutations<2,int>(); 65 | check_permutations<3,int>(); 66 | check_permutations<4,int>(); 67 | check_permutations<5,int>(); 68 | check_permutations<6,int>(); 69 | check_permutations<7,int>(); 70 | check_permutations<8,int>(); 71 | check_permutations<9,int>(); 72 | check_randomized<10,int> (RUNS); 73 | check_randomized<11,int> (RUNS); 74 | check_randomized<12,int> (RUNS); 75 | check_randomized<13,int> (RUNS); 76 | check_randomized<14,int> (RUNS); 77 | check_randomized<15,int> (RUNS); 78 | check_randomized<16,int> (RUNS); 79 | 80 | // sorted vector 81 | SortedVector s1 ({ 4,3,2,1, 9,8,7,6 }); 82 | TASSERT (s1.sorted ()); 83 | TASSERT (4 == *s1.find (4)); 84 | TASSERT (s1.end() == s1.find (5)); 85 | TASSERT (6 == *const_cast&> (s1).find (6)); 86 | TASSERT (false == s1.contains (5)); 87 | s1.insert (5); 88 | TASSERT (5 == *s1.find (5)); 89 | TASSERT (true == s1.contains (5)); 90 | s1.data()[1] = 1; 91 | TASSERT (s1.sorted () == false && s1.sorted (true) == true); 92 | TASSERT (s1[0] == s1[1]); 93 | const size_t col = s1.collapse(); 94 | TASSERT (col == 1 && s1[0] != s1[1]); 95 | TASSERT (s1.collapse() == 0); 96 | erase_if (s1, [] (auto v) { return v & 1; }); 97 | TASSERT (s1.size() == 3 && s1[0] == 4 && s1[1] == 6 && s1[2] == 8); 98 | s1.clear(); 99 | TASSERT (s1.size() == 0 && s1.sorted()); 100 | } 101 | 102 | } // Anon 103 | -------------------------------------------------------------------------------- /ase/storage.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_STORAGE_HH__ 3 | #define __ASE_STORAGE_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | class Storage { 10 | protected: 11 | virtual ~Storage () = 0; 12 | public: 13 | enum StorageFlags { 14 | NONE = 0, 15 | AUTO_ZSTD = 1, 16 | }; 17 | }; 18 | 19 | class StorageWriter : public Storage { 20 | class Impl; 21 | std::shared_ptr impl_; 22 | public: 23 | explicit StorageWriter (StorageFlags = StorageFlags::NONE); 24 | virtual ~StorageWriter (); 25 | // Writer API 26 | Error open_for_writing (const String &filename); 27 | Error open_with_mimetype (const String &filename, const String &mimetype); 28 | Error store_file_data (const String &filename, const String &buffer, bool alwayscompress = false); 29 | Error store_file (const String &filename, const String &ondiskpath, bool maycompress = true); 30 | Error close (); 31 | Error remove_opened (); 32 | }; 33 | 34 | class StorageReader : public Storage { 35 | class Impl; 36 | std::shared_ptr impl_; 37 | public: 38 | explicit StorageReader (StorageFlags = StorageFlags::NONE); 39 | virtual ~StorageReader (); 40 | // Reader API 41 | Error open_for_reading (const String &filename); 42 | void search_dir (const String &dirname); 43 | bool has_file (const String &filename); 44 | StringS list_files (); 45 | String stringread (const String &filename, ssize_t maxlength = -1); 46 | Error close (); 47 | }; 48 | 49 | class StreamReader { 50 | public: 51 | virtual ~StreamReader (); 52 | virtual String name () const = 0; 53 | virtual ssize_t read (void *buffer, size_t len) = 0; 54 | virtual bool close () = 0; 55 | static constexpr size_t buffer_size = 131072; ///< Recommended buffer size. 56 | }; 57 | 58 | StreamReaderP stream_reader_from_file (const String &file); 59 | StreamReaderP stream_reader_zip_member (const String &archive, const String &member, Storage::StorageFlags f = Storage::AUTO_ZSTD); 60 | 61 | class StreamWriter { 62 | public: 63 | virtual ~StreamWriter (); 64 | virtual String name () const = 0; 65 | virtual ssize_t write (const void *buffer, size_t len) = 0; 66 | virtual bool close () = 0; 67 | static constexpr size_t buffer_size = 131072; ///< Recommended buffer size. 68 | }; 69 | 70 | StreamWriterP stream_writer_create_file (const String &filename, int mode = 0644); 71 | 72 | String anklang_cachedir_create (); 73 | void anklang_cachedir_cleanup (const String &cachedir); 74 | void anklang_cachedir_clean_stale (); 75 | 76 | } // Ase 77 | 78 | #endif // __ASE_STORAGE_HH__ 79 | -------------------------------------------------------------------------------- /ase/track.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_TRACK_HH__ 3 | #define __ASE_TRACK_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | /// Ase::Track implementation. 10 | class TrackImpl : public DeviceImpl, public virtual Track { 11 | DeviceP chain_, midi_prod_; 12 | ClipImplS clips_; 13 | uint midi_channel_ = 0; 14 | ASE_DEFINE_MAKE_SHARED (TrackImpl); 15 | friend class ProjectImpl; 16 | virtual ~TrackImpl (); 17 | protected: 18 | String fallback_name () const override; 19 | void serialize (WritNode &xs) override; 20 | public: 21 | class ClipScout; 22 | explicit TrackImpl (ProjectImpl&, bool masterflag); 23 | void _activate () override; 24 | void _deactivate () override; 25 | AudioProcessorP _audio_processor () const override; 26 | void _set_event_source (AudioProcessorP esource) override; 27 | void _set_parent (GadgetImpl *parent) override; 28 | DeviceInfo device_info () override; 29 | ProjectImpl* project () const; 30 | bool is_master () const override { return MASTER_TRACK & gadget_flags(); } 31 | int32 midi_channel () const override { return midi_channel_; } 32 | void midi_channel (int32 midichannel) override; 33 | ClipS launcher_clips () override; 34 | DeviceP access_device () override; 35 | MonitorP create_monitor (int32 ochannel) override; 36 | void update_clips (); 37 | ssize_t clip_index (const ClipImpl &clip) const; 38 | int clip_succession (const ClipImpl &clip) const; 39 | TelemetryFieldS telemetry () const override; 40 | enum Cmd { STOP, START, }; 41 | void queue_cmd (CallbackS&, Cmd cmd, double arg = 0); 42 | void queue_cmd (DCallbackS&, Cmd cmd); 43 | enum { NONE = -1 }; 44 | }; 45 | 46 | /// MIDI clip playback succession generator. 47 | class TrackImpl::ClipScout { 48 | friend class TrackImpl; 49 | std::vector indices_; 50 | int last_ = -1; 51 | public: 52 | enum { NONE = TrackImpl::NONE, }; 53 | // constructors 54 | explicit ClipScout () noexcept; 55 | void setup (const std::vector &indices); 56 | int advance (int previous); 57 | void update (const ClipScout &other); 58 | void reset (); 59 | }; 60 | 61 | } // Ase 62 | 63 | #endif // __ASE_TRACK_HH__ 64 | -------------------------------------------------------------------------------- /ase/wave.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_WAVE_HH__ 3 | #define __ASE_WAVE_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | class WaveWriter { 10 | public: 11 | virtual ~WaveWriter (); 12 | virtual String name () const = 0; 13 | virtual ssize_t write (const float *frames, size_t n_frames) = 0; 14 | virtual bool close () = 0; 15 | }; 16 | using WaveWriterP = std::shared_ptr; 17 | 18 | WaveWriterP wave_writer_create_wav (int rate, int channels, const String &filename, int mode = 0664, uint8_t n_bits = 32); 19 | 20 | WaveWriterP wave_writer_create_opus (int rate, int channels, const String &filename, int mode = 0664, int complexity = 10, float bitrate = 128); 21 | String wave_writer_opus_version (); 22 | 23 | WaveWriterP wave_writer_create_flac (int rate, int channels, const String &filename, int mode = 0664, int compresion = 9); 24 | String wave_writer_flac_version (); 25 | 26 | } // Ase 27 | 28 | #endif // __ASE_WAVE_HH__ 29 | -------------------------------------------------------------------------------- /ase/websocket.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_WEBSOCKET_HH__ 3 | #define __ASE_WEBSOCKET_HH__ 4 | 5 | #include 6 | 7 | namespace Ase { 8 | 9 | class WebSocketServer; 10 | using WebSocketServerP = std::shared_ptr; 11 | 12 | class WebSocketConnection : public virtual std::enable_shared_from_this { 13 | friend class WebSocketServer; 14 | protected: 15 | virtual ~WebSocketConnection () = 0; 16 | struct Info { std::function header; StringS subs; String local, remote; int lport = 0, rport = 0; }; 17 | Info get_info (); 18 | public: 19 | bool is_open () const; 20 | String nickname (); 21 | virtual int validate (); ///< Return true to allow opened(). 22 | virtual void failed (); ///< Never folloed by opened(). 23 | virtual void opened (); ///< Pairs with closed(). 24 | virtual void http_request (); ///< Only if opened. 25 | virtual void message (const String &message); ///< Only if opened. 26 | virtual void closed (); ///< Pairs with opened(). 27 | virtual void log (const String &message); 28 | bool send_text (const String &message); ///< Returns true if text message was sent. 29 | bool send_binary (const String &blob); ///< Returns true if binary blob was sent. 30 | struct Internals; 31 | private: 32 | Internals &internals_; 33 | uint64_t internals_mem_[17]; 34 | protected: 35 | explicit WebSocketConnection (Internals &internals, int logflags); 36 | const int logflags_ = 0; 37 | }; 38 | using WebSocketConnectionP = std::shared_ptr; 39 | 40 | class WebSocketServer : public virtual std::enable_shared_from_this { 41 | protected: 42 | static WebSocketConnection::Internals& internals (WebSocketConnection &c) { return c.internals_; } 43 | virtual ~WebSocketServer(); 44 | public: 45 | using MakeConnection = std::function; 46 | using UnlistenCB = std::function; 47 | virtual void http_dir (const String &path) = 0; 48 | virtual void http_alias (const String &webdir, const String &path) = 0; 49 | virtual String map_url (const String &urlpath) = 0; 50 | virtual std::string url () const = 0; 51 | virtual void listen (const String &host = "", int port = 0, const UnlistenCB& = {}) = 0; 52 | virtual void reset () = 0; 53 | virtual void shutdown () = 0; 54 | static WebSocketServerP create (const MakeConnection &make, int logflags = 0); 55 | static String user_agent (); 56 | static String mime_type (const String &ext, bool utf8); 57 | static bool utf8_validate (const std::string &utf8string); 58 | }; 59 | 60 | } // Ase 61 | 62 | #endif // __ASE_WEBSOCKET_HH__ 63 | -------------------------------------------------------------------------------- /devices/GNUmakefile: -------------------------------------------------------------------------------- 1 | include ../misc/subdirs.mk 2 | -------------------------------------------------------------------------------- /devices/Makefile.mk: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | # variable for subdir sources 4 | devices/4ase.ccfiles ::= 5 | 6 | # subdir Makefiles add to devices/4ase.ccfiles 7 | include devices/blepsynth/Makefile.mk 8 | include devices/freeverb/Makefile.mk 9 | include devices/liquidsfz/Makefile.mk 10 | include devices/saturation/Makefile.mk 11 | 12 | # local sources 13 | devices/4ase.ccfiles += $(strip \ 14 | devices/colorednoise.cc \ 15 | ) 16 | 17 | # derive object files 18 | devices/4ase.objects ::= $(call BUILDDIR_O, $(devices/4ase.ccfiles)) 19 | 20 | # create object directories via explicit object dependency 21 | $(devices/4ase.objects): | $(sort $(dir $(devices/4ase.objects))) 22 | 23 | # include object dependencies 24 | include $(wildcard $(devices/4ase.objects:.o=.o.d)) 25 | 26 | # == devices/lint == 27 | devices/lint: 28 | $(QGEN) 29 | $Q $(RUNTS) misc/synsmell.ts $(wildcard devices/*.*[hc] devices/*/*.*[hc]) 30 | .PHONY: devices/lint 31 | lint: devices/lint 32 | -------------------------------------------------------------------------------- /devices/blepsynth/GNUmakefile: -------------------------------------------------------------------------------- 1 | include ../../misc/subdirs.mk 2 | -------------------------------------------------------------------------------- /devices/blepsynth/Makefile.mk: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | devices/4ase.ccfiles += $(strip \ 4 | devices/blepsynth/bleposcdata.cc \ 5 | devices/blepsynth/blepsynth.cc \ 6 | ) 7 | -------------------------------------------------------------------------------- /devices/blepsynth/linearsmooth.hh: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | #ifndef __ASE_DEVICES_LINEAR_SMOOTH_HH__ 3 | #define __ASE_DEVICES_LINEAR_SMOOTH_HH__ 4 | 5 | /* from liquidsfz utils */ 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace Ase { 12 | 13 | class LinearSmooth 14 | { 15 | float value_ = 0; 16 | float linear_value_ = 0; 17 | float linear_step_ = 0; 18 | uint total_steps_ = 1; 19 | uint steps_ = 0; 20 | public: 21 | void 22 | reset (uint rate, float time) 23 | { 24 | total_steps_ = std::max (rate * time, 1); 25 | } 26 | void 27 | set (float new_value, bool now = false) 28 | { 29 | if (now) 30 | { 31 | steps_ = 0; 32 | value_ = new_value; 33 | } 34 | else if (new_value != value_) 35 | { 36 | if (!steps_) 37 | linear_value_ = value_; 38 | 39 | linear_step_ = (new_value - linear_value_) / total_steps_; 40 | steps_ = total_steps_; 41 | value_ = new_value; 42 | } 43 | } 44 | float 45 | get_next() 46 | { 47 | if (!steps_) 48 | return value_; 49 | else 50 | { 51 | steps_--; 52 | linear_value_ += linear_step_; 53 | return linear_value_; 54 | } 55 | } 56 | bool 57 | is_constant() 58 | { 59 | return steps_ == 0; 60 | } 61 | }; 62 | 63 | } 64 | 65 | #endif /* __ASE_DEVICES_LINEAR_SMOOTH_HH__ */ 66 | -------------------------------------------------------------------------------- /devices/freeverb/GNUmakefile: -------------------------------------------------------------------------------- 1 | include ../../misc/subdirs.mk 2 | -------------------------------------------------------------------------------- /devices/freeverb/Makefile.mk: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | devices/4ase.ccfiles += $(strip \ 4 | devices/freeverb/freeverb.cc \ 5 | ) 6 | $>/devices/freeverb/freeverb.o: EXTRA_FLAGS ::= -Wno-unused-function 7 | -------------------------------------------------------------------------------- /devices/freeverb/allpass.cpp: -------------------------------------------------------------------------------- 1 | // Allpass filter implementation 2 | // 3 | // Written by Jezar at Dreampoint, June 2000 4 | // http://www.dreampoint.co.uk 5 | // This code is public domain 6 | 7 | #include "allpass.hpp" 8 | 9 | allpass::allpass() 10 | { 11 | bufidx = 0; 12 | } 13 | 14 | void allpass::setbuffer(float *buf, int size) 15 | { 16 | buffer = buf; 17 | bufsize = size; 18 | } 19 | 20 | void allpass::mute() 21 | { 22 | for (int i=0; i=bufsize) bufidx = 0; 42 | 43 | return output; 44 | } 45 | 46 | #endif//_allpass 47 | 48 | //ends -------------------------------------------------------------------------------- /devices/freeverb/comb.cpp: -------------------------------------------------------------------------------- 1 | // Comb filter implementation 2 | // 3 | // Written by Jezar at Dreampoint, June 2000 4 | // http://www.dreampoint.co.uk 5 | // This code is public domain 6 | 7 | #include "comb.hpp" 8 | 9 | comb::comb() 10 | { 11 | filterstore = 0; 12 | bufidx = 0; 13 | } 14 | 15 | void comb::setbuffer(float *buf, int size) 16 | { 17 | buffer = buf; 18 | bufsize = size; 19 | } 20 | 21 | void comb::mute() 22 | { 23 | for (int i=0; i=bufsize) bufidx = 0; 49 | 50 | return output; 51 | } 52 | 53 | #endif //_comb_ 54 | 55 | //ends 56 | -------------------------------------------------------------------------------- /devices/freeverb/denormals.h: -------------------------------------------------------------------------------- 1 | // Macro for killing denormalled numbers 2 | // 3 | // Written by Jezar at Dreampoint, June 2000 4 | // http://www.dreampoint.co.uk 5 | // Based on IS_DENORMAL macro by Jon Watte 6 | // This code is public domain 7 | 8 | #ifndef _denormals_ 9 | #define _denormals_ 10 | 11 | #define undenormalise(sample) if(((*(unsigned int*)&sample)&0x7f800000)==0) sample=0.0f 12 | 13 | #endif//_denormals_ 14 | 15 | //ends 16 | -------------------------------------------------------------------------------- /devices/freeverb/revmodel.hpp: -------------------------------------------------------------------------------- 1 | // Reverb model declaration 2 | // 3 | // Written by Jezar at Dreampoint, June 2000 4 | // http://www.dreampoint.co.uk 5 | // This code is public domain 6 | 7 | #ifndef _revmodel_ 8 | #define _revmodel_ 9 | 10 | #include "comb.hpp" 11 | #include "allpass.hpp" 12 | #include "tuning.h" 13 | 14 | class revmodel 15 | { 16 | public: 17 | revmodel(); 18 | void mute(); 19 | void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); 20 | void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip); 21 | void setroomsize(float value); 22 | float getroomsize(); 23 | void setdamp(float value, int mode); 24 | float getdamp(); 25 | void setwet(float value); 26 | float getwet(); 27 | void setdry(float value); 28 | float getdry(); 29 | void setwidth(float value); 30 | float getwidth(); 31 | void setmode(float value); 32 | float getmode(); 33 | private: 34 | void update(); 35 | private: 36 | float gain; 37 | float roomsize,roomsize1; 38 | float damp,damp1; 39 | int dampmode; 40 | float wet,wet1,wet2; 41 | float dry; 42 | float width; 43 | float mode; 44 | 45 | // The following are all declared inline 46 | // to remove the need for dynamic allocation 47 | // with its subsequent error-checking messiness 48 | 49 | // Comb filters 50 | comb combL[numcombs]; 51 | comb combR[numcombs]; 52 | 53 | // Allpass filters 54 | allpass allpassL[numallpasses]; 55 | allpass allpassR[numallpasses]; 56 | 57 | // Buffers for the combs 58 | float bufcombL1[combtuningL1]; 59 | float bufcombR1[combtuningR1]; 60 | float bufcombL2[combtuningL2]; 61 | float bufcombR2[combtuningR2]; 62 | float bufcombL3[combtuningL3]; 63 | float bufcombR3[combtuningR3]; 64 | float bufcombL4[combtuningL4]; 65 | float bufcombR4[combtuningR4]; 66 | float bufcombL5[combtuningL5]; 67 | float bufcombR5[combtuningR5]; 68 | float bufcombL6[combtuningL6]; 69 | float bufcombR6[combtuningR6]; 70 | float bufcombL7[combtuningL7]; 71 | float bufcombR7[combtuningR7]; 72 | float bufcombL8[combtuningL8]; 73 | float bufcombR8[combtuningR8]; 74 | 75 | // Buffers for the allpasses 76 | float bufallpassL1[allpasstuningL1]; 77 | float bufallpassR1[allpasstuningR1]; 78 | float bufallpassL2[allpasstuningL2]; 79 | float bufallpassR2[allpasstuningR2]; 80 | float bufallpassL3[allpasstuningL3]; 81 | float bufallpassR3[allpasstuningR3]; 82 | float bufallpassL4[allpasstuningL4]; 83 | float bufallpassR4[allpasstuningR4]; 84 | }; 85 | 86 | #endif//_revmodel_ 87 | 88 | //ends 89 | -------------------------------------------------------------------------------- /devices/freeverb/tuning.h: -------------------------------------------------------------------------------- 1 | // Reverb model tuning values 2 | // 3 | // Written by Jezar at Dreampoint, June 2000 4 | // http://www.dreampoint.co.uk 5 | // This code is public domain 6 | 7 | #ifndef _tuning_ 8 | #define _tuning_ 9 | 10 | const int numcombs = 8; 11 | const int numallpasses = 4; 12 | const float muted = 0; 13 | const float fixedgain = 0.015f; 14 | const float scalewet = 3; 15 | const float scaledry = 2; 16 | const float scaledamp = 0.4f; 17 | const float scaleroom = 0.28f; 18 | const float offsetroom = 0.7f; 19 | const float initialroom = 0.5f; 20 | const float initialdamp = 0.5f; 21 | const float initialwet = 1/scalewet; 22 | const float initialdry = 0; 23 | const float initialwidth = 1; 24 | const float initialmode = 0; 25 | const float freezemode = 0.5f; 26 | const int stereospread = 23; 27 | 28 | // These values assume 44.1KHz sample rate 29 | // they will probably be OK for 48KHz sample rate 30 | // but would need scaling for 96KHz (or other) sample rates. 31 | // The values were obtained by listening tests. 32 | const int combtuningL1 = 1116; 33 | const int combtuningR1 = 1116+stereospread; 34 | const int combtuningL2 = 1188; 35 | const int combtuningR2 = 1188+stereospread; 36 | const int combtuningL3 = 1277; 37 | const int combtuningR3 = 1277+stereospread; 38 | const int combtuningL4 = 1356; 39 | const int combtuningR4 = 1356+stereospread; 40 | const int combtuningL5 = 1422; 41 | const int combtuningR5 = 1422+stereospread; 42 | const int combtuningL6 = 1491; 43 | const int combtuningR6 = 1491+stereospread; 44 | const int combtuningL7 = 1557; 45 | const int combtuningR7 = 1557+stereospread; 46 | const int combtuningL8 = 1617; 47 | const int combtuningR8 = 1617+stereospread; 48 | const int allpasstuningL1 = 556; 49 | const int allpasstuningR1 = 556+stereospread; 50 | const int allpasstuningL2 = 441; 51 | const int allpasstuningR2 = 441+stereospread; 52 | const int allpasstuningL3 = 341; 53 | const int allpasstuningR3 = 341+stereospread; 54 | const int allpasstuningL4 = 225; 55 | const int allpasstuningR4 = 225+stereospread; 56 | 57 | #endif//_tuning_ 58 | 59 | //ends 60 | 61 | -------------------------------------------------------------------------------- /devices/liquidsfz/Makefile.mk: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | devices/4ase.ccfiles += $(strip \ 4 | devices/liquidsfz/liquidsfz.cc \ 5 | ) 6 | -------------------------------------------------------------------------------- /devices/saturation/Makefile.mk: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | devices/4ase.ccfiles += $(strip \ 4 | devices/saturation/saturation.cc \ 5 | ) 6 | -------------------------------------------------------------------------------- /doc/GNUmakefile: -------------------------------------------------------------------------------- 1 | include ../misc/subdirs.mk 2 | -------------------------------------------------------------------------------- /doc/NAV.md: -------------------------------------------------------------------------------- 1 | 2 | * Introducing Anklang 3 | * [About Anklang](index.md) 4 | * [Tutorial 1: Starting to Play Notes](tut-play.md) 5 | * Howto Guides 6 | * [Howto: Setting Up Audio (Linux)](how-audio.md) 7 | * References 8 | * [ANKLANG(1) Manual Page](anklang.1.md) 9 | * [JS Scripting](scripting-docs.md) 10 | * JS Utils 11 | * jsdocsmd/*.md 12 | * JS Component Files 13 | * jsdocsmd/b/*.md 14 | * C++ API 15 | * [C++ Class-Tree](class-tree.md) 16 | * [C++ Classes](doxygen/classes.html) 17 | * [C++ Files](doxygen/files.html) 18 | * [C++ Functions](doxygen/namespace_ase.html#func-members) 19 | * Design & Development 20 | * [Development Overview](ch-development.md) 21 | * UI Development 22 | * [Web Components](ch-component.md) 23 | * [JS UI Components](jsdocs.md) 24 | * Implementation Details 25 | * [Making Releases](ch-releasing.md) 26 | * [Interpolation Notes](ch-appendix.md) 27 | -------------------------------------------------------------------------------- /doc/anklang.1.md: -------------------------------------------------------------------------------- 1 | % ANKLANG(1) anklang-0 | Anklang Manual 2 | 3 | # NAME 4 | anklang - Music composition and modular synthesis application 5 | 6 | 7 | # SYNOPSIS 8 | **anklang** [*OPTIONS*] [*FILES*...] 9 | 10 | 11 | # DESCRIPTION 12 | 13 | **Anklang** is a digital audio synthesis application for live creation 14 | and composition of music and other audio material. 15 | It is released as free software under the MPL-2.0. 16 | 17 | Anklang comes with synthesis devices which can be arranged in tracks 18 | and controlled via MIDI input devices or pre-programmed clips which 19 | contain MIDI notes. 20 | 21 | The Anklang sound engine is a dedicated process which is controlled 22 | by a user interface based on web technologies that can be run in a 23 | special process (like electron) or modern browsers 24 | like **firefox**(1) or **google-chrome**(1). 25 | 26 | # OPTIONS 27 | 28 | Anklang supports short and long options which start with two dashes ('-'). 29 | 30 | **--** 31 | : Stop argument processing, treat remaining arguments as files. 32 | 33 | **-h**, **--help** 34 | : Show a help message about command line usage. 35 | 36 | **--fatal-warnings** 37 | : Abort on warnings and failing assertions, useful for test modes. 38 | 39 | **--check** 40 | : Execute internal tests and benchmarks. 41 | 42 | **--disable-randomization** 43 | : Enable deterministic random numbers for test modes. 44 | 45 | **-v**, **--version** 46 | : Print information about the program version. 47 | 48 | # SEE ALSO 49 | 50 | [**Anklang Website**](http://anklang.testbit.eu){.external} 51 | -------------------------------------------------------------------------------- /doc/ch-releasing.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | Releases of the Anklang project are hosted on GitHub under [Anklang Releases](https://github.com/tim-janik/anklang/releases/). 4 | A release encompasses a distribution tarball that has the release version number baked into the misc/version.sh script. 5 | 6 | ## Versioning 7 | 8 | The Anklang project uses **`MAJOR.MINOR.MICRO[.DEVEL][-SUFFIX]`** version numbers with the folloing uses: 9 | - **`MAJOR`** - The major number is currently 0, so all bets are off. It is planned to signify major changes to users. 10 | - **`MINOR`** - The minor number indicates significant changes, often these are user visible improvements. 11 | - **`MICRO`** - The micro number increases with every release. 12 | - **`DEVEL`** - The devel part is optional and increases with every new commit, it numbers builds between official releases. 13 | The presence of the `[.DEVEL]` part indicates a version ordered *after* its corresponding `MAJOR.MINOR.MICRO` release. 14 | - **`SUFFIX`** - An optional suffix is sometimes used for e.g. release candidates. 15 | The presence of the `[-SUFFIX]` part indicates a version ordered *before* its corresponding `MAJOR.MINOR.MICRO` release. 16 | 17 | Git tags are used to store release versions, development versions are derived from those tags similar to how `git describe` works. 18 | The current version can always be obtained by invoking `misc/version.sh`. 19 | 20 | ## Release Assets 21 | 22 | The script `misc/mkassets.sh` can be used to create and clean up a release build directory and it triggers the necessary rules to 23 | create a distribution tarball and to build the release assets. All assets are built from the distribution tarball without any 24 | Git dependency. Producing a distribution tarball depends on Git however. 25 | -------------------------------------------------------------------------------- /doc/ch-scripting.md: -------------------------------------------------------------------------------- 1 | 2 | # Anklang Scripting 3 | 4 | The functionality of Anklang can be extended by user provided scripts. 5 | The scripts are written in Javascript and run as part of the User Interface threads. 6 | 7 | Scripts can extend editing functionality or implement new controllers. 8 | The exact functions provided by a script are registered at script startup time. 9 | 10 | -------------------------------------------------------------------------------- /doc/copyright: -------------------------------------------------------------------------------- 1 | Upstream-Name: anklang 2 | Upstream-Contact: Tim Janik 3 | Source: https://github.com/tim-janik/anklang/ 4 | 5 | Files: 6 | .git* 7 | .github/workflows/*.* 8 | LICENSE 9 | Makefile.mk 10 | NEWS.md 11 | README.md 12 | ase/*.c 13 | ase/*.cc 14 | ase/*.h 15 | ase/*.hh 16 | ase/Makefile.mk 17 | devices/* 18 | devices/blepsynth/* 19 | devices/freeverb/Makefile.mk 20 | devices/freeverb/freeverb.cc 21 | devices/liquidsfz/* 22 | devices/saturation/* 23 | doc/*.* 24 | doc/copyright 25 | doc/javascript/*.* 26 | doc/style/*.* 27 | electron/*.* 28 | images/*.* 29 | images/icons/*.* 30 | images/knobs/*.* 31 | jsonipc/*.* 32 | jsonipc/Makefile 33 | misc/*.* 34 | misc/AppRun 35 | misc/blame-lines 36 | package.json 37 | rand/hotspots.sh 38 | ui/*.* 39 | ui/assets/*.* 40 | ui/b/*.* 41 | ui/cursors/*.* 42 | x11test/*.* 43 | Copyright: Copyright (C) 2020-Present The Anklang Project Contributors 44 | License: MPL-2.0 45 | 46 | Files: 47 | **/GNUmakefile 48 | GNUmakefile 49 | ase/mime-types.hh 50 | devices/freeverb/* 51 | jsonipc/jsonipc.js 52 | jsonipc/testjsonipc.cc 53 | misc/checkcrlist.py 54 | x11test/*.json 55 | Copyright: Copyright (C) 2020-Present The Anklang Project Contributors 56 | License: Unlicense 57 | 58 | License: MPL-2.0 59 | Mozilla Public License 2.0 60 | https://www.mozilla.org/MPL/2.0/ 61 | https://opensource.org/licenses/MPL-2.0 62 | 63 | License: Unlicense 64 | This is free and unencumbered software released into the public domain. 65 | . 66 | Anyone is free to copy, modify, publish, use, compile, sell, or 67 | distribute this software, either in source code form or as a compiled 68 | binary, for any purpose, commercial or non-commercial, and by any 69 | means. 70 | . 71 | In jurisdictions that recognize copyright laws, the author or authors 72 | of this software dedicate any and all copyright interest in the 73 | software to the public domain. We make this dedication for the benefit 74 | of the public at large and to the detriment of our heirs and 75 | successors. We intend this dedication to be an overt act of 76 | relinquishment in perpetuity of all present and future rights to this 77 | software under copyright law. 78 | . 79 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 80 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 81 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 82 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 83 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 84 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 85 | OTHER DEALINGS IN THE SOFTWARE. 86 | . 87 | For more information, please refer to 88 | -------------------------------------------------------------------------------- /doc/copyright.ini: -------------------------------------------------------------------------------- 1 | # Anklang project config file for mkcopyright.py 2 | # Dedicated to the Public Domain under the Unlicense: https://unlicense.org/UNLICENSE 3 | 4 | [debian/copyright] 5 | Upstream-Name: anklang 6 | Upstream-Contact: Tim Janik 7 | Source: https://github.com/tim-janik/anklang/ 8 | Disclaimer: Autogenerated by anklang/misc/mkcopyright.py 9 | 10 | [ignore] 11 | files = 12 | LICENSE 13 | 14 | [Unlicense] 15 | patterns = 16 | (.*/)?GNUmakefile 17 | 18 | [CC-PDDC] 19 | files = 20 | devices/freeverb/readme.txt 21 | devices/freeverb/tuning.h 22 | devices/freeverb/denormals.h 23 | patterns = 24 | devices/freeverb/.*\.hpp 25 | devices/freeverb/.*\.cpp 26 | 27 | [MPL-2.0] 28 | files = 29 | .gitattributes 30 | .gitignore 31 | .gitmodules 32 | NEWS.md 33 | README.md 34 | devices/blepsynth/TODO 35 | doc/highlights.theme 36 | doc/how-audio.md 37 | doc/index.md 38 | doc/javascript/mathjax.js 39 | doc/jsdocrc.json 40 | doc/template.diff 41 | doc/tut-play.md 42 | images/README.md 43 | misc/anklang-mime.xml 44 | package.json 45 | patterns = 46 | .*/ch-.*\.md 47 | doc/.*\.1\.md 48 | x11test/.*\.json 49 | -------------------------------------------------------------------------------- /doc/filt-docs2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 3 | """ 4 | Pandoc filter to increase heading level, convert all level 3+ 5 | headers to Strong text and level 2 headers to SmallCaps. 6 | """ 7 | from pandocfilters import toJSONFilter, Emph, Strong, SmallCaps, Para, Header 8 | 9 | def behead (key, value, format_, meta): 10 | if key == 'Header': 11 | if value[0] >= 3: 12 | return Para ([ Strong (value[2]) ]) 13 | elif value[0] >= 2: 14 | return Para ([ SmallCaps (value[2]) ]) 15 | # increase heading levels 16 | level, content, attr = value 17 | # content[1] += [ 'unnumbered' ] # causes "Duplicate identifier" warnings 18 | return Header (min (6, level + 2), content, attr) 19 | 20 | if __name__ == "__main__": 21 | toJSONFilter (behead) 22 | -------------------------------------------------------------------------------- /doc/filt-man.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 3 | """ 4 | Pandoc filter to convert all level 2+ headers to paragraphs 5 | with emphasized text. 6 | """ 7 | from pandocfilters import toJSONFilter, Emph, Strong, SmallCaps, Para, Header 8 | 9 | def behead (key, value, format_, meta): 10 | if key == 'Header': 11 | if value[0] >= 2: 12 | return Para ([ SmallCaps (value[2]) ]) 13 | else: 14 | return Para ([ Strong (value[2]) ]) 15 | # alt: increase heading levels 16 | level, content, attr = value 17 | # content[1] += [ 'unnumbered' ] # causes "Duplicate identifier" warnings 18 | return Header (min (6, level + 2), content, attr) 19 | 20 | if __name__ == "__main__": 21 | toJSONFilter (behead) 22 | -------------------------------------------------------------------------------- /doc/how-audio.md: -------------------------------------------------------------------------------- 1 | # Howto: Setting Up Audio Devices in Anklang on Linux 2 | 3 | Anklang relies on a properly configured audio system to function correctly. 4 | This guide will walk you through the steps to ensure your audio devices are set up and working with Anklang on Linux. 5 | 6 | ### Step 1: Verify Audio Device Drivers 7 | 8 | Most Linux systems come with ALSA (Advanced Linux Sound Architecture) device drivers for audio devices. 9 | Additionally, they usually run either [Pulseaudio](https://wiki.archlinux.org/title/PulseAudio) or [Pipewire](https://docs.pipewire.org/) as their sound system. 10 | Make sure your audio devices are properly detected and configured on your system. 11 | You can do this by: 12 | 13 | * Configuring audio devices via GNOME ++"Settings"++ → ++"🎵 Sound"++ (if you're using GNOME) 14 | * Running [`pavucontrol`(1)](https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Users/Troubleshooting/#baselinetest) from the terminal to adjust audio settings 15 | 16 | ### Step 2: Adjust Audio Device Volume and Microphone Sensitivity 17 | 18 | Use [`alsamixer`(1)](https://man.archlinux.org/man/alsamixer.1) from the terminal to adjust audio device volume and microphone sensitivity: 19 | 20 | * Press ++"F6"++ to select the sound card 21 | * Press ++"F5"++ to display all channels 22 | * Use cursor keys to navigate and adjust volume 23 | * Press ++"M"++ to mute/unmute channels 24 | 25 | ### Step 3: Configure Anklang Audio Preferences 26 | 27 | Once your audio devices are confirmed to work correctly, start Anklang and open the ++"🖿"++ "File" menu. Select ++"Preferences"++ (or press ++"Ctrl-,"++) to access the preferences dialog. All detected devices and some special entries are in the the "PCM driver" menu: 28 | 29 | * ++"PulseAudio Sound Server"++ or a similar entry will appear if a user space sound system is detected. 30 | * ++"Null PCM Driver"++ will simulate an audio device that cannot be listened to (useful for testing or development) 31 | * ++"Automatic Driver Selection"++ will first look for a running user space sound system like PulseAudio/Pipewire and route sound through it. If that's not available, the first non-busy sound device is automatically selected (USB and onboard devices being preferred over HDMI devices) 32 | * Individual devices can also be selected directly from the menu 33 | 34 | ### Step 4: Select and Test Your Audio Device 35 | 36 | Choose your desired audio device from the "PCM driver" drop-down menu. Changing the audio device selection has an instant effect. To test, load a (demo) song and confirm playback works through the selected device. If you encounter issues, ensure your audio devices are properly configured in your Linux system (Step 1 and 2). 37 | 38 | By following these steps, you should be able to successfully set up your audio devices in Anklang on Linux. If you still encounter problems or have questions, please don't hesitate to 39 | [report them](https://github.com/tim-janik/anklang/issues) or [join the discussions](https://github.com/tim-janik/anklang/discussions). 40 | 41 | Happy creating with Anklang! 42 | -------------------------------------------------------------------------------- /doc/javascript/mathjax.js: -------------------------------------------------------------------------------- 1 | 2 | window.MathJax = { 3 | tex: { 4 | inlineMath: [["\\(", "\\)"]], 5 | displayMath: [["\\[", "\\]"]], 6 | processEscapes: true, 7 | processEnvironments: true 8 | }, 9 | options: { 10 | ignoreHtmlClass: ".*|", 11 | processHtmlClass: "arithmatex" 12 | } 13 | }; 14 | 15 | document$.subscribe (() => { 16 | MathJax.startup.output.clearCache() 17 | MathJax.typesetClear() 18 | MathJax.texReset() 19 | MathJax.typesetPromise() 20 | }); 21 | -------------------------------------------------------------------------------- /doc/jsdoc-slashes.cjs: -------------------------------------------------------------------------------- 1 | /// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | "use strict"; 3 | 4 | /* This fils is loaded as CJS module from jsdoc, via jsdocrc.json */ 5 | 6 | /// Export jsdoc plugin to convert '/// comment\n' into '/** comment */\n' 7 | exports.handlers = { 8 | beforeParse: (arg) => { 9 | // arg = { filename, source } 10 | arg.source = convert_slashes (arg.source); 11 | } 12 | }; 13 | 14 | /// Convert triple slash comments in *src_text* 15 | /// Turns triple slash comments into jsdoc style comments. 16 | /// Joins comments from multiple consecutive triple slash lines. 17 | function convert_slashes (src_text) { 18 | return src_text.replace (/((\n|^)\s*\/\/\/([^\n]*)\n)+/gm, lines => { 19 | lines = lines.replace (/\*\//g, '*​/'); // escape premature */ via Zwsp 20 | lines = lines.replace (/\/\/\//, '/**'); // replace *first* /// with /** 21 | lines = lines.replace (/(\n\s*)\/\/\//g, '$1 *'); // replace consecutive /// 22 | lines = lines.replace (/\n?$/, '*/\n'); // terminate with */ 23 | return lines; 24 | }); 25 | } 26 | 27 | // test with: node slashcomment.js INPUTFILE 28 | if (process.argv[1].replace (/.*\//, '') == 'slashcomment.js') { 29 | const fs = require ('fs'); 30 | const source = String (fs.readFileSync (process.argv[2])); 31 | console.log (convert_slashes (source)); 32 | } 33 | -------------------------------------------------------------------------------- /doc/jsdocrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "jsdoc-tsimport-plugin", 4 | "doc/jsdoc-slashes.cjs" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /doc/pandoc-pdf.tex: -------------------------------------------------------------------------------- 1 | % This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | % latex setup for pandoc PDF conversions 4 | 5 | % !TeX encoding = UTF-8 6 | \usepackage[utf8]{inputenc} 7 | 8 | % allow colorization commands 9 | \usepackage{xcolor} 10 | \definecolor{darkblue}{HTML}{000080} 11 | 12 | % Configure TOC links 13 | \usepackage{hyperref} 14 | \hypersetup{ 15 | colorlinks=true, 16 | linktoc=all, % link sections and subsections 17 | urlcolor=darkblue, 18 | linkcolor=darkblue, % colorize links to stand out 19 | } 20 | 21 | % allow custom justification 22 | \usepackage{ragged2e} 23 | 24 | % Provide page titles 25 | \usepackage{fancyhdr} 26 | \pagestyle{fancy} 27 | 28 | % Adjust Code sections 29 | % But --listings breaks utf-8: https://en.wikibooks.org/wiki/LaTeX/Source_Code_Listings#Encoding_issue 30 | %\lstset{basicstyle=\ttfamily,basicstyle=\ttfamily,breaklines=true} 31 | %\lstset{framextopmargin=50pt,frame=leftline} 32 | 33 | % Adjust Abstract width (and indent): https://tex.stackexchange.com/questions/151583/how-to-adjust-the-width-of-abstract/151589 34 | \renewenvironment{abstract} 35 | {\small 36 | \vspace{9em}% Abstract headspace 37 | \begin{center} 38 | \bfseries \abstractname\vspace{-.5em}\vspace{0pt} 39 | \end{center} 40 | \list{}{% 41 | \setlength{\leftmargin}{3.5cm}% Abstract margin 42 | \setlength{\rightmargin}{\leftmargin}% 43 | }% 44 | \item\relax} 45 | {\endlist} 46 | 47 | % Turn fontfamily lookup / encoding errors into warnings 48 | \PassOptionsToPackage{warn}{textcomp} 49 | 50 | % Note, use of the 'titlesec' package requires: pandoc --variable=subparagraph 51 | \usepackage{titlesec} 52 | %\titleformat{\subsubsection}{\normalfont\normalsize\bfseries}{\thesubsubsection}{1em}{} 53 | %\titleformat{\subsection}[runin]{}{}{}{}[] 54 | \newcommand{\sectionbreak}{\clearpage} 55 | % Prevent runin for 4th level "headings" 56 | \titleformat{\paragraph}{\normalfont\normalsize\bfseries}{\theparagraph}{1em}{} 57 | \titlespacing{\paragraph}{0pt}{*1}{-0.5ex} 58 | %\titlespacing*{\paragraph}{0pt}{3.25ex plus 1ex minus .2ex}{-1ex} 59 | % Fix Ubuntu messing up section numbers in titlesec.sty 60 | % https://tex.stackexchange.com/questions/299969/titlesec-loss-of-section-numbering-with-the-new-update-2016-03-15/300259#300259 61 | \usepackage{etoolbox} 62 | \makeatletter 63 | \patchcmd{\ttlh@hang}{\parindent\z@}{\parindent\z@\leavevmode}{}{} 64 | \patchcmd{\ttlh@hang}{\noindent}{}{}{} 65 | \makeatother 66 | 67 | % Make document title bold 68 | \makeatletter 69 | \patchcmd{\@maketitle}{\@title}{\bfseries\@title}{}{} 70 | \patchcmd{\@maketitle}{\@author}{\bfseries\@author}{}{} 71 | \patchcmd{\@maketitle}{\@date}{\bfseries\@date}{}{} 72 | \makeatother 73 | 74 | \usepackage{flafter} % Prevent figures floating up 75 | \usepackage[section]{placeins} % Prevent tables floating out of section (only works with non-empty sections) 76 | 77 | \usepackage[font=small,labelfont=bf]{caption} 78 | \captionsetup[table]{position=below} 79 | 80 | % Prevent Figures floating out of sections 81 | % http://stackoverflow.com/questions/16626462/figure-position-in-markdown-when-converting-to-pdf-with-knitr-and-pandoc/33801326#33801326 82 | \usepackage{float} 83 | \floatstyle{boxed} 84 | \restylefloat{figure} 85 | \floatstyle{plaintop} 86 | \restylefloat{table} 87 | 88 | \let\origfigure\figure 89 | \let\endorigfigure\endfigure 90 | \renewenvironment{figure}[1][2] { 91 | \expandafter\origfigure\expandafter[H] 92 | } { 93 | \endorigfigure 94 | } 95 | 96 | % force newline after definition list items 97 | \usepackage{enumitem} 98 | \setlist[description]{style=nextline} 99 | -------------------------------------------------------------------------------- /doc/style/Makefile.mk: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | include $(wildcard $>/doc/style/*.d) 3 | ALL_TARGETS += doc/style/all 4 | doc/style/all: 5 | 6 | doc/style/install.files ::= 7 | doc/style/faketex.css ::= $>/doc/style/faketex.css 8 | 9 | # == doc/style/mathjax == 10 | $>/doc/style/mathjax-tex-svg.js: doc/style/mathjax-config.js node_modules/.npm.done | $>/doc/style/ 11 | $(QGEN) 12 | $Q cat $< node_modules/mathjax/es5/tex-svg-full.js > $@.tmp 13 | $Q mv $@.tmp $@ 14 | doc/style/install.files += $>/doc/style/mathjax-tex-svg.js 15 | 16 | # == doc/style/faketex.css == 17 | $(doc/style/faketex.css): doc/style/faketex.scss doc/style/features.scss node_modules/.npm.done | $>/doc/style/ 18 | $(QGEN) 19 | $Q node_modules/.bin/sass --embed-source-map $< $@.tmp 20 | $Q mv $@.tmp $@ 21 | doc/style/install.files += $(doc/style/faketex.css) 22 | 23 | DOC/STYLE/SEDFONTS ::= -e "s/, *url('[^()']*\.woff') *format('woff')//" -e "s|url('\./files|url('./|" 24 | 25 | # == doc/style/charis-sil.css == 26 | $>/doc/style/charis-sil.css: node_modules/.npm.done | $>/doc/style/ 27 | $(QGEN) 28 | $Q $(CP) node_modules/@fontsource/charis-sil/files/*.woff2 $>/doc/style/ 29 | $Q sed node_modules/@fontsource/charis-sil/index.css $(DOC/STYLE/SEDFONTS) > $@.tmp 30 | $Q mv $@.tmp $@ 31 | doc/style/install.files += $>/doc/style/charis-sil.css 32 | 33 | # == doc/style/inconsolata.css == 34 | $>/doc/style/inconsolata.css: node_modules/.npm.done | $>/doc/style/ 35 | $(QGEN) 36 | $Q $(CP) node_modules/@fontsource/inconsolata/files/inconsolata-latin-ext-*-normal.woff2 $>/doc/style/ 37 | $Q sed node_modules/@fontsource/inconsolata/latin-ext.css $(DOC/STYLE/SEDFONTS) > $@.tmp 38 | $Q mv $@.tmp $@ 39 | doc/style/install.files += $>/doc/style/inconsolata.css 40 | 41 | # == doc/style/ installation == 42 | doc/style/all: $(doc/style/install.files) 43 | doc/style/installdir ::= $(DESTDIR)$(pkgdir)/doc 44 | ALL_TARGETS += $(doc/style/install.files) 45 | $(call INSTALL_DIR_RULE, doc/style/install.files, $(doc/style/installdir), $>/doc/style/) 46 | -------------------------------------------------------------------------------- /doc/style/features.scss: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | /// Enable hyphenation for MS, FF and Chrome based browsers. 4 | @mixin hyphenate() { 5 | // based on https://kenneth.io/blog/2012/03/04/word-wrapping-hypernation-using-css/ 6 | -ms-word-break: break-all; word-break: break-all; // old IE: break everywhere 7 | @media (min--moz-device-pixel-ratio:0) { // Firefox selector 8 | word-break: normal; // FF: use hyphenation, not breaks 9 | } 10 | word-break: break-word; // Webkit extension: refine break positions 11 | -webkit-hyphens: auto; -moz-hyphens: auto; 12 | -o-hyphens: auto; -ms-hyphens: auto; // enable CSS3 hyphenation 13 | hyphens: auto; // suggest breaks with soft hyphen, U+00AD: ­ 14 | overflow-wrap: break-word; // CSS3: prevent overflow by breaking 15 | } 16 | 17 | /// Enable justified text with hyphenation. 18 | @mixin justify() { 19 | text-align: justify; 20 | @include hyphenate; 21 | } 22 | 23 | /// Print: avoid otp margin, but force page break 24 | @mixin pagebreak() { 25 | @media print { margin-top: 0; page-break-before: always; } 26 | } 27 | 28 | 29 | $_monospace_block_elements: pre, listing, xmp, plaintext; 30 | $_monospace_inline_elements: tt, code, kbd, samp; 31 | $_monospace_elements: #{$_monospace_block_elements}, #{$_monospace_inline_elements}; 32 | 33 | @mixin widthem-from-minwidth($font-size: 16px, $max-em: 99) { 34 | @for $i from 1 through ceil($max-em - 1) { 35 | @media only screen and (min-width: floor($font-size * ($i + 0.5))) { width: #{$i}em; } 36 | } 37 | @media only screen and (min-width: floor($font-size * ($max-em + 0.5))) { width: #{$max-em}em; } 38 | } 39 | 40 | $media-width-pxlist: ( 41 | 2048, 42 | 1600, 43 | 1536, 44 | 1366, 45 | 1280, 46 | 1200, 47 | 1112, 48 | 1024, 49 | 906, 50 | 834, 51 | 812, 52 | 800, 53 | 768, 54 | 736, 55 | 667, 56 | 640, 57 | 601, 58 | 592, 59 | 568, 60 | 480, 61 | 414, 62 | 384, 63 | 375, 64 | 360, 65 | 320, 66 | 281, 67 | 218, 68 | ); 69 | 70 | @mixin media-width-max-em($font-size: 16px, $max-em: 99) { 71 | width: $max-em * 1em; 72 | @each $i in $media-width-pxlist { 73 | $target-em: $i * math.div( 1px, $font-size) * 1em - 1em; 74 | @if $target-em < $max-em { 75 | @media only screen and (max-width: #{$i}px) { width: $target-em; } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /doc/style/mathjax-config.js: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | "use strict"; 3 | 4 | window.MathJax = { 5 | options: { 6 | enableMenu: true, // allow menu to allow copy-and-paste 7 | menuOptions: { 8 | settings: { 9 | explorer: false, 10 | texHints: true, // put TeX-related attributes on MathML 11 | semantics: false, // put original format in tag in MathML 12 | zoom: 'Click', // or 'Click' or 'DoubleClick' as zoom trigger 13 | zscale: '250%', // zoom scaling factor 14 | renderer: 'SVG', // or 'SVG' 15 | alt: false, // true if ALT required for zooming 16 | cmd: false, // true if CMD required for zooming 17 | ctrl: false, // true if CTRL required for zooming 18 | shift: false, // true if SHIFT required for zooming 19 | scale: 1, // scaling factor for all math 20 | collapsible: false, // true if complex math should be collapsible 21 | inTabOrder: false, // true if tabbing includes math 22 | }, 23 | }, 24 | }, 25 | startup: { 26 | pageReady: () => { // Called when MathJax and page are ready 27 | MathJax.startup.defaultPageReady (); 28 | }, 29 | ready: () => { // Called when components are loaded 30 | // MathJax is loaded, but not yet initialized 31 | MathJax.startup.defaultReady (); 32 | // MathJax is initialized, and the initial typeset is queued 33 | MathJax.startup.promise.then (() => { /* MathJax initial typesetting complete */ }); 34 | // Disable submenus with missing components 35 | const m = MathJax.startup.document.menu.menu; 36 | for (const id of [ "Accessibility", "Settings", "Language" ]) 37 | m.findID (id).disable(); 38 | }, 39 | }, 40 | }; 41 | 42 | // Stale MathJax settings can break rendering 43 | localStorage.clear(); 44 | window.addEventListener ('beforeunload', _ => localStorage.clear()); 45 | -------------------------------------------------------------------------------- /doc/style/mkdocs.css: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 */ 2 | 3 | /* Use indentation to show nav titles */ 4 | main.md-main .md-sidebar nav.md-nav > ul { 5 | margin-left: 0.5em; 6 | } 7 | /* Darken greyish nav titles */ 8 | body:not([data-md-color-media*='dark']) 9 | main.md-main .md-sidebar nav.md-nav 10 | label { color: black; } 11 | 12 | /* Hide mkdocs-live-edit-plugin buttons, use Shift+Ctrl+E */ 13 | .md-content article .live-edit-controls:not(.live-edit-editing) { 14 | width: 0; height: 0; overflow: hidden; 15 | border: 0; margin: 0; padding: 0; 16 | } 17 | -------------------------------------------------------------------------------- /doc/style/onload.html: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /doc/template.diff: -------------------------------------------------------------------------------- 1 | --- template.orig 2021-01-22 14:58:50.360691470 +0100 2 | +++ template.html 2021-01-22 14:59:16.632563545 +0100 3 | @@ -54,2 +54,6 @@ 4 |

$date$

5 | $endif$ 6 | +$if(abstract)$ 7 | +

Abstract
8 | +$abstract$

9 | +$endif$ 10 | -------------------------------------------------------------------------------- /doc/tut-play.md: -------------------------------------------------------------------------------- 1 | ## Tutorial 1: Starting to Play Notes 2 | 3 | **Starting to Play Notes with Anklang** 4 | 5 | Welcome to Anklang, an open-source digital audio workstation for live music composition and audio synthesis. 6 | This tutorial will guide you through the basics of creating a new project and adding your first sounds. 7 | 8 | ### Step 1: Create a New Project 9 | 10 | To start, navigate to the ++"🖿"++ "File" menu and select ++"New Project"++. This will create a fresh canvas for your audio creation. 11 | 12 | ### Step 2: Add a Track 13 | 14 | To see the track menu, right-click into the track area, e.g. on the "Master" track and select ++"Add Track"++ to create a new track for your project. This will be the foundation for your audio composition. 15 | 16 | ### Step 3: Add a Device 17 | 18 | In the lower left corner of the window, click the :material-equalizer: "Show Device Stack" icon. This will display the device stack for your track. Click the ++"\+"++ menu icon to create a new ++"BlepSynth"++ device in the track. BlepSynth is a built-in synthesizer that will generate sound for your track. 19 | 20 | ### Step 4: Access the Piano Roll Editor 21 | 22 | In the lower left corner, next to the "Show Device Stack" icon, click the :musical_score: "Show Piano Roll Editor" icon. This will open the piano roll editor, where you can create and arrange notes for your track. You can also use the ++"^"++ hotkey to toggle between the "Show Device Stack" and "Show Piano Roll Editor" modes, depending on what you need to work on. 23 | 24 | ### Step 5: Draw Notes 25 | 26 | In the piano roll editor, click the piano roll tool menu and select ++"✎ Pen 3"++ or press the ++3++ key on your keyboard when inside the piano roll. This will allow you to draw notes directly onto the piano roll. Use mouse clicks to add notes to the current clip. Experiment with different pitches and durations to create your melody. 27 | 28 | ### Step 6: Play Your Creation 29 | 30 | Click the ++"▶"++ playback button or press the ++"Space"++ bar to hear your composition come to life. The same button can be used to stop playback when you're done listening. 31 | 32 | ### Step 7: Experiment with Sound 33 | 34 | Click the :material-equalizer: "Show Device Stack" icon again to return to the device stack. During playback, tweak the knobs of the BlepSynth device to explore different sounds and timbres. Adjust the parameters to shape your audio and create unique textures. 35 | 36 | That's it! You've just started creating music with Anklang. Feel free to explore the interface, add more tracks, devices, and clips to develop your project further. If you encounter any issues or have ideas for improvement, please don't hesitate to [report them](https://github.com/tim-janik/anklang/issues) or [join the discussions](https://github.com/tim-janik/anklang/discussions). 37 | 38 | Happy creating! 39 | -------------------------------------------------------------------------------- /electron/Makefile.mk: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | include $(wildcard $>/electron/*.d) 3 | ALL_TARGETS += electron/all 4 | electron/all: 5 | 6 | electron/js.sources ::= electron/main.js electron/preload.js $>/ui/anklang.png 7 | 8 | # == electron/anklang == 9 | $>/electron/anklang: $(electron/js.sources) electron/Makefile.mk node_modules/.npm.done 10 | $(QGEN) 11 | $Q rm -f -r $(@D) 12 | $Q $(CP) -r node_modules/electron/dist/ $(@D) 13 | $Q chmod -x $>/electron/lib*.so* 14 | $Q rm $(@D)/resources/default_app.asar 15 | $Q mkdir -p $(@D)/resources/app 16 | $Q $(CP) $(electron/js.sources) $(@D)/resources/app/ 17 | $Q echo '{ "private": true,' > $(@D)/resources/app/package.json 18 | $Q echo ' "name": "anklang/electron",' >> $(@D)/resources/app/package.json 19 | $Q echo ' "version": "$(version_short)",' >> $(@D)/resources/app/package.json 20 | $Q echo ' "main": "main.js",' >> $(@D)/resources/app/package.json 21 | $Q echo ' "mode": "$(MODE)" }' >> $(@D)/resources/app/package.json 22 | $Q chmod g-w -R $>/electron/ 23 | $Q mv $(@D)/electron $@ 24 | electron/all: $>/electron/anklang 25 | 26 | # == installation == 27 | electron/installdir ::= $(pkgdir)/electron 28 | .PHONY: electron/install 29 | electron/install: $>/electron/anklang 30 | @$(QECHO) INSTALL '$(DESTDIR)$(electron/installdir)/.' 31 | $Q rm -f -r '$(DESTDIR)$(electron/installdir)' 32 | $Q $(INSTALL) -d $(DESTDIR)$(electron/installdir)/ 33 | $Q $(CP) -Rp $>/electron/* $(DESTDIR)$(electron/installdir) 34 | $Q $(call INSTALL_SYMLINK, '../electron/anklang', '$(DESTDIR)$(pkgdir)/bin/anklang') 35 | $Q $(INSTALL) -d '$(DESTDIR)$(bindir)/' 36 | $Q rm -f '$(DESTDIR)$(bindir)/anklang' 37 | $Q ln -s -r '$(DESTDIR)$(pkgdir)/bin/anklang' '$(DESTDIR)$(bindir)/anklang' 38 | install: electron/install 39 | .PHONY: electron/uninstall 40 | electron/uninstall: 41 | @$(QECHO) REMOVE '$(DESTDIR)$(electron/installdir)/.' 42 | $Q rm -f -r '$(DESTDIR)$(electron/installdir)' 43 | $Q rm -f '$(DESTDIR)$(pkgdir)/bin/anklang' 44 | $Q $(RMDIR_P) '$(DESTDIR)$(pkgdir)/bin' || true 45 | $Q rm -f '$(DESTDIR)$(bindir)/anklang' 46 | uninstall: electron/uninstall 47 | -------------------------------------------------------------------------------- /electron/preload.js: -------------------------------------------------------------------------------- 1 | // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 'use strict'; 3 | 4 | process.once ('loaded', () => { 5 | const { contextBridge, ipcRenderer } = require ('electron'); 6 | const Electron = { 7 | async call (func, ...args) { return await ipcRenderer.invoke (func, args); }, 8 | }; 9 | contextBridge.exposeInMainWorld ('__Electron__', Electron); 10 | }); 11 | -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | # AnklangIcons - Cursors & icon font for Anklang 2 | 3 | SVG based cursor and icon drawings for the Anklang project. 4 | 5 | ## SCSS Cursor Variables 6 | 7 | Cursors from [Anklang/images/cursors/](https://github.com/tim-janik/anklang/tree/master/images/cursors/) 8 | are transformed into SCSS variables holding `CSS cursor` values. For instance: 9 | 10 | ```SCSS 11 | // SCSS cursor use: 12 | @import 'anklangicons/AnklangCursors.scss'; 13 | * { cursor: $anklang-cursor-pen; } 14 | // CSS result: 15 | * { cursor: url(data:image/svg+xml;base64,Abc…xyZ) 3 28, default; } 16 | ``` 17 | 18 | The cursors are SVG drawings, layed out at 32x32 pixels, 19 | and `anklangicons.sh` contains the hotspot coordinates. 20 | 21 | ## Icon font 22 | 23 | Icons from [Anklang/images/icons/](https://github.com/tim-janik/anklang/tree/master/images/icons/) 24 | are transformed into a WOFF2 font and a CSS file `AnklangIcons.css` to be used like this: 25 | 26 | ```HTML 27 | 28 | 29 | 30 | 31 | 32 | ``` 33 | 34 | The icons are SVG drawings and layed out at 24x24 pixels. 35 | 36 | The required set of build tools is fairly involved. Building makes use of Inkscape, 37 | ImageMagick and various SVG/font related npm packages, so prebuilt artifacts are 38 | uploaded and can be fetched from: 39 | https://github.com/tim-janik/anklang/releases/tag/buildassets-v0 40 | 41 | ## LICENSE 42 | 43 | Unless noted otherwise, the AnklangIcons source code forms are licensed 44 | [MPL-2.0](http://mozilla.org/MPL/2.0) 45 | -------------------------------------------------------------------------------- /images/anklangicons.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 3 | set -Eeuo pipefail 4 | # set -x 5 | 6 | # == Config == 7 | S=1 # scaling 8 | W=32 # width 9 | H=32 # height 10 | ORIGIN=$(pwd) 11 | 12 | # == args == 13 | SCRIPTNAME=${0##*/} ; die() { [ -z "$*" ] || echo "$SCRIPTNAME: $*" >&2; exit 9 ; } 14 | HOTSPOT=false 15 | while test $# -ne 0 ; do 16 | case "$1" in 17 | --hotspot) HOTSPOT=true ;; 18 | *) die "unknown argument: '$1'" ;; 19 | esac 20 | shift 21 | done 22 | 23 | # == Create TMP == 24 | TMP=$(pwd) && TMP="$TMP/build.$UID" 25 | test -e $TMP/node_modules/.bin/svgtofont || ( 26 | rm -rf $TMP/ 27 | mkdir -p $TMP/ 28 | cd $TMP/ 29 | npm i svgtofont svgo@2.8.0 ) 30 | rm -rf $TMP/svgs/ $TMP/font/ 31 | mkdir -p $TMP/svgs/ 32 | 33 | # == Dist basics == 34 | rm -fr anklangicons/ 35 | test "${1:-}" != clean || exit 36 | mkdir -p anklangicons/ 37 | pandoc README.md -t markdown -o anklangicons/README 38 | ANKLANGVERSION=$(git describe --match 'v[0-9]*.[0-9]*.*[0-9a]' --abbrev=8 --long) 39 | echo >> anklangicons/README 40 | echo "Build with anklangicons.sh from anklang-$ANKLANGVERSION" >> anklangicons/README 41 | 42 | # == Optimize Glyph == 43 | optimize_glyph() 44 | { 45 | F="$1" 46 | G="$TMP/svgs/${F##*/}" 47 | test "$F" = "$G" || cp "$F" "$G" 48 | $TMP/node_modules/.bin/svgo -i $G --multipass -o $G 49 | } 50 | 51 | # == Populate Glyphs == 52 | for SVG in icons/*.svg ; do 53 | cp "$SVG" $TMP/svgs/ 54 | done 55 | 56 | # == Turn Glyphs into Paths == 57 | for SVG in $TMP/svgs/*.svg ; do 58 | inkscape $SVG --verb=EditSelectAll --verb=ObjectToPath --verb=StrokeToPath --verb=FileSave --verb=FileQuit 59 | inkscape $SVG --vacuum-defs --export-plain-svg $SVG 60 | done 61 | 62 | # == Optimize Glyphs == 63 | for SVG in $TMP/svgs/*.svg ; do 64 | optimize_glyph "$SVG" 65 | done 66 | 67 | # == Create Font == 68 | ( cd $TMP/ 69 | node_modules/.bin/svgtofont --sources svgs/ --output ./font --fontName AnklangIcons 70 | # Keep just woff2 reference in .css 71 | sed -r '/\burl/s/(AnklangIcons\.woff2)\?t=[0-9]+/\1/; s/[^ ]*url(.*woff2.*)[,;]/src:URL\1;/; /url/d; s/src:URL/src: url/' -i font/AnklangIcons.css 72 | ) 73 | cp $TMP/font/AnklangIcons.css $TMP/font/AnklangIcons.woff2 anklangicons/ 74 | 75 | # == AnklangCursors.scss == 76 | mv anklangicons/cursors.tmp anklangicons/AnklangCursors.scss 77 | 78 | # == dist == 79 | DATE=$(date +%y%m%d) 80 | fgrep -s -q "# $DATE" $TMP/lastminor || echo "0" > $TMP/lastminor 81 | MINOR=$(sed 's/ #.*//' $TMP/lastminor) 82 | MINOR=$(( $MINOR + 1 )) && echo "$MINOR # $DATE" > $TMP/lastminor 83 | TARBALL=anklangicons-$DATE.$MINOR.tgz 84 | tar --mtime="$DATE" -cf $TARBALL --exclude '*.hotspot.png' anklangicons/ 85 | $HOTSPOT || rm -r anklangicons/ 86 | tar tvf $TARBALL 87 | ls -l $TARBALL 88 | sha256sum $TARBALL 89 | 90 | # == upload cmd == 91 | echo "# hub release edit -m '' -a $ORIGIN/$TARBALL buildassets-v0" 92 | -------------------------------------------------------------------------------- /images/icons/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /images/icons/menumore.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /images/knobs/Makefile.mk: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | include $(wildcard $>/images/knobs/*.d) 3 | 4 | images/knobs/cknob.color ::= '\#00ddff' 5 | images/knobs/cknob.args ::= -w 40 -s 14 -g 6 -n 193 6 | 7 | # == .deps == 8 | $>/images/knobs/.deps: images/knobs/Makefile.mk | $>/images/knobs/ 9 | $(QGEN) 10 | $Q rsvg-convert --version | egrep 'rsvg-convert\b.* [2-9][0-9]*\.[0-9]' >/dev/null || \ 11 | { echo "$@: missing rsvg-convert version >= 2" ; false ; } 12 | $Q convert --version | egrep 'ImageMagick\b.* [2-9][0-9]*\.[0-9]' >/dev/null || \ 13 | { echo "$@: missing ImageMagick version >= 6" ; false ; } 14 | $Q touch $@ 15 | 16 | # == cknob193u.png == 17 | $>/images/knobs/cknob193u.png: images/knobs/cknob.svg images/knobs/mksprite.py $>/images/knobs/.deps 18 | $(QGEN) 19 | $Q $(PYTHON3) images/knobs/mksprite.py -q --sprite -u $(images/knobs/cknob.color) $(images/knobs/cknob.args) $< $(@:.png=.aux) 20 | $Q $(RM) $(@:.png=.aux).tmp-* 21 | $Q mv $(@:.png=.aux).png $@ 22 | ALL_TARGETS += $>/images/knobs/cknob193u.png 23 | 24 | # == cknob193b.png == 25 | $>/images/knobs/cknob193b.png: images/knobs/cknob.svg images/knobs/mksprite.py $>/images/knobs/.deps 26 | $(QGEN) 27 | $Q $(PYTHON3) images/knobs/mksprite.py -q --sprite -b $(images/knobs/cknob.color) $(images/knobs/cknob.args) $< $(@:.png=.aux) 28 | $Q $(RM) $(@:.png=.aux).tmp-* 29 | $Q mv $(@:.png=.aux).png $@ 30 | ALL_TARGETS += $>/images/knobs/cknob193b.png 31 | -------------------------------------------------------------------------------- /jsonipc/Makefile: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | all: testjsonipc 4 | 5 | builddir ?= out 6 | CXX = ccache clang++ 7 | #CXX = ccache g++ -Wno-nonnull-compare -Wno-address 8 | CXXFLAGS = -std=gnu++17 -Wall -O0 -g 9 | OBJECTS = testjsonipc.o 10 | DEFS = -DSTANDALONE 11 | DEPS = jsonipc.hh Makefile 12 | 13 | INCLUDES = -I../external/rapidjson/include/ 14 | 15 | $(OBJECTS): %.o: %.cc $(DEPS) 16 | $(CXX) $(DEFS) $(CXXFLAGS) $(INCLUDES) -c $< 17 | .INTERMEDIATE: $(OBJECTS) 18 | 19 | testjsonipc: $(OBJECTS) Makefile 20 | $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) -lboost_system 21 | .INTERMEDIATE: testjsonipc 22 | 23 | check: testjsonipc 24 | ./testjsonipc 25 | 26 | clean: 27 | rm -f $(OBJECTS) testjsonipc 28 | -------------------------------------------------------------------------------- /misc/AppRun: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 3 | 4 | SCRIPTNAME="$(readlink -f "$0")" 5 | APPROOT="${SCRIPTNAME%/*}" 6 | 7 | export PATH="$APPROOT/usr/bin/:$PATH" 8 | export LD_LIBRARY_PATH="$APPROOT/usr/lib/:$LD_LIBRARY_PATH" 9 | export XDG_DATA_DIRS="$APPROOT/usr/share/:$XDG_DATA_DIRS" 10 | 11 | exec "$APPROOT/usr/bin/anklang" "$@" 12 | -------------------------------------------------------------------------------- /misc/Dockerfile.arch: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | FROM archlinux 3 | 4 | # Upgrade, install dependencies, clean up to shrink docker image 5 | RUN pacman -Sy --noconfirm archlinux-keyring && pacman-key --init && pacman-key --populate \ 6 | && pacman -Syu --noconfirm \ 7 | && pacman -S --noconfirm \ 8 | sudo fakeroot git base-devel \ 9 | ccache cmake unzip \ 10 | clang llvm boost ctags \ 11 | nodejs npm \ 12 | python-lxml \ 13 | imagemagick \ 14 | graphviz \ 15 | flac libvorbis jack mpg123 lame \ 16 | gtk2 \ 17 | pandoc python-pandocfilters \ 18 | cppcheck \ 19 | python-pip doxygen openssh \ 20 | gtk3 nss \ 21 | xorg-server-xvfb ffmpeg xorg-twm \ 22 | && yes | pacman -Scc 23 | 24 | # Castxml is not provided by pacman 25 | RUN cd /tmp && git clone https://github.com/CastXML/CastXML.git && cd CastXML && cmake . && make -j && sudo make install 26 | 27 | # Provide a fast JS package manager 28 | RUN npm install -g pnpm@latest 29 | 30 | # Become non-root, use Github action VM user: uid=1001(runner) gid=127(docker) 31 | RUN groupadd -g 127 docker && useradd -u 1001 -g 127 -m -d /github/home -s /bin/bash runner \ 32 | && echo "runner ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/runner 33 | USER runner 34 | 35 | # docker build -t ghcr.io/tim-janik/anklang-ci:arch-latest -f misc/Dockerfile.arch misc 36 | # docker run -ti --rm -v $PWD:/hostwd ghcr.io/tim-janik/anklang-ci:arch-latest 37 | -------------------------------------------------------------------------------- /misc/GNUmakefile: -------------------------------------------------------------------------------- 1 | include ../misc/subdirs.mk 2 | -------------------------------------------------------------------------------- /misc/anklang-mime.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Anklang Project 6 | 7 | 8 | -------------------------------------------------------------------------------- /misc/anklang.desktop: -------------------------------------------------------------------------------- 1 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 2 | 3 | [Desktop Entry] 4 | Type=Application 5 | Name=Anklang 6 | Exec=$(pkgdir)/bin/anklang %f 7 | Terminal=false 8 | Icon=anklang 9 | MimeType=application/x-anklang; 10 | StartupWMClass="anklang/electron" 11 | Categories=Audio;Midi;Mixer;Music;Sequencer;AudioVideoEditing;Player;AudioVideo; 12 | Keywords=MIDI;Sequencer;Synthesizer;Mixing;Effects;Notation;Composition;Live;DAW;Studio;ALSA;JACK;Pulse;LADSPA;LV2;SF2;SFZ;WAV;SoundFont;FluidSynth;Synth;Sound;Tracker;Audio;Editor;Electron; 13 | Comment=MIDI Sequencer and Synthesizer for Editing Drums, Melodic Notes and Live Audio Sessions 14 | -------------------------------------------------------------------------------- /misc/blame-lines: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 3 | import sys, re, os, subprocess 4 | 5 | cache = {} 6 | b_log = False 7 | 8 | first = 1 9 | if len (sys.argv) > first and sys.argv[first] == '-b': 10 | b_log = True 11 | first += 1 12 | 13 | last = ('', -1) 14 | for inputfile in sys.argv[first:]: 15 | fout = sys.stdout.buffer 16 | if b_log: 17 | outputfile = os.path.splitext (inputfile)[0] + '.blame' 18 | fout = open (outputfile, 'wb') 19 | print ("%s: creating blame file: %s" % (inputfile, outputfile)) 20 | for bline in open (inputfile, 'rb'): 21 | line = bline.decode ('utf8') 22 | m = re.match (r'([^:]+):([0-9]+):([0-9]+:)?\s', line) 23 | if m: 24 | g = m.groups() 25 | f, l = g[0], int (g[1]) 26 | if last != (f, l): 27 | last = (f, l) 28 | blame = cache.get (last) 29 | if not blame: 30 | result = subprocess.run (['git', '--no-pager', 'blame', '-e', '-l', '-L%d,%d' % (l, l), '--', f], stdout = subprocess.PIPE, stderr = subprocess.STDOUT) 31 | blame = result.stdout.decode().rstrip() 32 | blame = re.sub (r'^\^?([a-f0-9]+)[^(]*\(([^)]+)\).*', r'\1 \2', blame) 33 | p = blame.find (')') 34 | if p > 0: 35 | blame = blame[:p+1] 36 | cache[last] = blame 37 | line = '%s:%d: %s' % (f, l, blame) 38 | fout.write ((line.rstrip() + '\n').encode ('utf8')) 39 | fout.write (bline) 40 | -------------------------------------------------------------------------------- /misc/checkinsertions.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 3 | set -Eeuo pipefail #-x 4 | 5 | RANGE="${1:-origin/trunk..HEAD}" # HEAD~9..HEAD 6 | DPATT=TO"DO|FIX"ME 7 | WORDS="${2:-$DPATT}" # keyword extended regex 8 | 9 | git diff --name-only -z "$RANGE" | ( 10 | while IFS= read -r -d $'\0' FILE; do 11 | git diff "$RANGE" -U0 -- "$FILE" | 12 | sed -rne $'s\5^\5'"$FILE"$':\5' -e "/:\+.*([#*/]|