{"id":12570,"date":"2026-02-06T14:59:22","date_gmt":"2026-02-06T12:59:22","guid":{"rendered":"https:\/\/store.algosyntax.com\/?post_type=asx-lms-tutorial-cpt&#038;p=12570"},"modified":"2026-03-09T12:23:19","modified_gmt":"2026-03-09T10:23:19","slug":"metasound-node-configuration-operator-data","status":"publish","type":"asx-lms-tutorial-cpt","link":"https:\/\/store.algosyntax.com\/tutorials\/unreal-engine\/metasound-node-configuration-operator-data\/","title":{"rendered":"MetaSound Node Configuration API | Serialized Operator Data"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"12570\" class=\"elementor elementor-12570\" data-elementor-post-type=\"asx-lms-tutorial-cpt\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c17f24e e-flex e-con-boxed e-con e-parent\" data-id=\"c17f24e\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-22d0078 elementor-widget elementor-widget-heading\" data-id=\"22d0078\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">What this tutorial covers<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3041cb7 elementor-widget elementor-widget-text-editor\" data-id=\"3041cb7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"131\" data-end=\"176\">How to leverage the new Metasounds Node Configuration in UE 5.7 to create serialized\/persistant metasound node data.<\/p><p data-start=\"168\" data-end=\"213\">This article assumes you already know how to:<\/p><ol data-start=\"215\" data-end=\"504\"><li data-start=\"215\" data-end=\"278\"><p data-start=\"218\" data-end=\"278\">Create a custom MetaSound operator (<code data-start=\"254\" data-end=\"275\">TExecutableOperator<\/code>)<\/p><\/li><li data-start=\"279\" data-end=\"371\"><p data-start=\"282\" data-end=\"371\">Define a vertex interface (<code data-start=\"309\" data-end=\"327\">FVertexInterface<\/code>, <code data-start=\"329\" data-end=\"347\">TInputDataVertex<\/code>, <code data-start=\"349\" data-end=\"368\">TOutputDataVertex<\/code>)<\/p><\/li><li data-start=\"372\" data-end=\"435\"><p data-start=\"375\" data-end=\"435\">Register nodes (<code data-start=\"391\" data-end=\"416\">METASOUND_REGISTER_NODE<\/code>, metadata, etc.)<\/p><\/li><li data-start=\"436\" data-end=\"504\"><p data-start=\"439\" data-end=\"504\">Use custom MetaSound data types (proxies, <code data-start=\"481\" data-end=\"490\">ReadRef<\/code>\/<code data-start=\"491\" data-end=\"501\">WriteRef<\/code>)<\/p><\/li><\/ol><p data-start=\"506\" data-end=\"554\">This tutorial focuses on what changed in UE 5.7:<\/p><ol data-start=\"556\" data-end=\"1085\"><li data-start=\"556\" data-end=\"624\"><p data-start=\"559\" data-end=\"624\"><strong data-start=\"559\" data-end=\"622\">What \u201cNode Configuration\u201d actually is at the document level<\/strong><\/p><\/li><li data-start=\"625\" data-end=\"720\"><p data-start=\"628\" data-end=\"720\"><strong data-start=\"628\" data-end=\"682\">How it enables persistent, per-node-instance state<\/strong> (serialized in the MetaSound asset)<\/p><\/li><li data-start=\"721\" data-end=\"811\"><p data-start=\"724\" data-end=\"811\"><strong data-start=\"724\" data-end=\"754\">Why <code data-start=\"730\" data-end=\"745\">IOperatorData<\/code> exists<\/strong> and why you should treat it as an Audio Thread payload<\/p><\/li><li data-start=\"812\" data-end=\"900\"><p data-start=\"815\" data-end=\"900\">How to integrate <code data-start=\"832\" data-end=\"869\">FMetaSoundFrontendNodeConfiguration<\/code> into an existing custom node<\/p><\/li><li data-start=\"901\" data-end=\"991\"><p data-start=\"904\" data-end=\"991\">Why <code data-start=\"908\" data-end=\"921\">TNodeFacade<\/code> is usually required for configuration-based operator data injection<\/p><\/li><li data-start=\"992\" data-end=\"1085\"><p data-start=\"995\" data-end=\"1085\">A brief, bounded introduction to <code data-start=\"1028\" data-end=\"1056\">OverrideDefaultInterface()<\/code> and when it is appropriate<\/p><\/li><\/ol>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-d4475d1 e-flex e-con-boxed e-con e-parent\" data-id=\"d4475d1\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c7d629e elementor-widget elementor-widget-heading\" data-id=\"c7d629e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">1. The Problem UE Node Configuration In UE  5.7 Solves<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6a20a6f elementor-widget elementor-widget-text-editor\" data-id=\"6a20a6f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"1124\" data-end=\"1174\">Before UE 5.7, a custom MetaSound node did <strong data-start=\"1352\" data-end=\"1359\">not<\/strong> have a first-class way to store <strong data-start=\"1392\" data-end=\"1434\">custom per-node-instance data <\/strong>inside the MetaSound document.<\/p><p data-start=\"1124\" data-end=\"1174\">\u00a0<\/p><p data-start=\"1467\" data-end=\"1557\">If you wanted \u201ceach node instance has its own serialized data\u201d, there was no ready-made system to do that\/<\/p><p data-start=\"2223\" data-end=\"2344\">UE 5.7\u2019s \u201cNode Configuration\u201d system exists to solve these issues in a way that is aligned with MetaSound\u2019s architecture.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-5270e52 e-flex e-con-boxed e-con e-parent\" data-id=\"5270e52\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-5b8f5e9 elementor-widget elementor-widget-heading\" data-id=\"5b8f5e9\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">2. The NodeConfiguration Struct<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-83c0ccf elementor-widget elementor-widget-text-editor\" data-id=\"83c0ccf\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"2395\" data-end=\"2415\">There is a new struct you&#8217;ll have to inherit from to use this new API.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-a669818 elementor-widget elementor-widget-code-highlight\" data-id=\"a669818\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>struct FMetaSoundFrontendNodeConfiguration\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-41e60c1 elementor-widget elementor-widget-text-editor\" data-id=\"41e60c1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"2472\" data-end=\"2543\">This is not an operator concept. It is a <strong data-start=\"2513\" data-end=\"2542\">frontend\/document\/serialization concept<\/strong>.<\/p><p data-start=\"2545\" data-end=\"2579\">MetaSound has two relevant layers:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-dbfcea9 e-flex e-con-boxed e-con e-parent\" data-id=\"dbfcea9\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7fd29bd elementor-widget elementor-widget-heading\" data-id=\"7fd29bd\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">2.1 Frontend \/ Document Layer (Serialized, Editor-facing)<\/h3>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e4dc86a elementor-widget elementor-widget-text-editor\" data-id=\"e4dc86a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"2643\" data-end=\"2695\">This is where the MetaSound graph is stored as data:<\/p><ul data-start=\"2697\" data-end=\"2794\"><li data-start=\"2697\" data-end=\"2704\"><p data-start=\"2699\" data-end=\"2704\">Nodes<\/p><\/li><li data-start=\"2713\" data-end=\"2733\"><p data-start=\"2715\" data-end=\"2733\">Vertex definitions<\/p><\/li><li data-start=\"2753\" data-end=\"2794\"><p data-start=\"2755\" data-end=\"2794\"><strong data-start=\"2755\" data-end=\"2794\">Per-node configuration (new in 5.7)<\/strong><\/p><\/li><\/ul><p data-start=\"2796\" data-end=\"2930\">A Node Configuration is a <strong data-start=\"2822\" data-end=\"2877\">USTRUCT that is serialized with the MetaSound asset<\/strong> and is associated with a <strong data-start=\"2903\" data-end=\"2929\">specific node instance<\/strong>.<\/p><p data-start=\"2932\" data-end=\"2948\">So you can have:<\/p><ul data-start=\"2950\" data-end=\"3096\"><li data-start=\"2950\" data-end=\"2983\"><p data-start=\"2952\" data-end=\"2983\">Node A (same class as Node B)<\/p><\/li><li data-start=\"2984\" data-end=\"2994\"><p data-start=\"2986\" data-end=\"2994\">Node B<\/p><\/li><li data-start=\"2995\" data-end=\"3049\"><p data-start=\"2997\" data-end=\"3049\">Each has a different configuration struct instance<\/p><\/li><li data-start=\"3050\" data-end=\"3096\"><p data-start=\"3052\" data-end=\"3096\">Both serialize into the MetaSound document<\/p><\/li><\/ul><p data-start=\"3098\" data-end=\"3162\">This is the primary \u201cpersistent per node\u201d capability we want.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-8001333 e-flex e-con-boxed e-con e-parent\" data-id=\"8001333\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-d9a2c6b elementor-widget elementor-widget-heading\" data-id=\"d9a2c6b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">2.2 Runtime \/ Operator Layer (DSP, Audio thread)<\/h3>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0a3326f elementor-widget elementor-widget-text-editor\" data-id=\"0a3326f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"3217\" data-end=\"3250\">This is where MetaSound executes:<\/p><ul data-start=\"3252\" data-end=\"3485\"><li data-start=\"3252\" data-end=\"3289\"><p data-start=\"3254\" data-end=\"3289\"><code data-start=\"3254\" data-end=\"3265\">IOperator<\/code> instances do DSP work<\/p><\/li><li data-start=\"3290\" data-end=\"3338\"><p data-start=\"3292\" data-end=\"3338\">Operators run on the <strong data-start=\"3313\" data-end=\"3336\">Audio Render Thread<\/strong><\/p><\/li><li data-start=\"3339\" data-end=\"3399\"><p data-start=\"3341\" data-end=\"3399\">Operators are expected to be deterministic and efficient<\/p><\/li><li data-start=\"3400\" data-end=\"3485\"><p data-start=\"3402\" data-end=\"3485\">Operators must not rely on garbage-collected objects or editor-only document data<\/p><\/li><\/ul><p data-start=\"3487\" data-end=\"3619\">Node Configuration lives in the frontend\/document world. Operators run in runtime\/DSP world. The bridge between these two worlds is the function:<\/p><p data-start=\"3623\" data-end=\"3642\"><code data-start=\"3623\" data-end=\"3642\">GetOperatorData()<br \/><\/code><\/p><p data-start=\"3623\" data-end=\"3642\">we&#8217;ll discuss it later.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-a264f1e e-flex e-con-boxed e-con e-parent\" data-id=\"a264f1e\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4027f8e elementor-widget elementor-widget-heading\" data-id=\"4027f8e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">3. The IOperatorData class<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-20056b3 elementor-widget elementor-widget-text-editor\" data-id=\"20056b3\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p><code data-start=\"3735\" data-end=\"3750\">IOperatorData<\/code> is a payload class you create\u00a0 so that your <strong data-start=\"3791\" data-end=\"3841\">operator can consume node configuration safely<\/strong>. &#8220;Safely&#8221; as in thread safety. Its a copy of the data inside the NodeConfiguration basically.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-41f2da7 elementor-widget elementor-widget-heading\" data-id=\"41f2da7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">Why copy data instead of using NodeConfiguration struct directly:<\/h3>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8594e4a elementor-widget elementor-widget-text-editor\" data-id=\"8594e4a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"3886\" data-end=\"3920\">Operators run on the Audio Thread.<\/p><p data-start=\"3922\" data-end=\"4012\">USTRUCTs and UObjects are overwhelmingly treated as <strong data-start=\"3974\" data-end=\"4000\">Game Thread\u2013owned data<\/strong> and may be:<\/p><ul data-start=\"4014\" data-end=\"4197\"><li data-start=\"4014\" data-end=\"4052\"><p data-start=\"4016\" data-end=\"4052\">modified by editor\/game thread code,<\/p><\/li><li data-start=\"4053\" data-end=\"4091\"><p data-start=\"4055\" data-end=\"4091\">subject to garbage collection rules,<\/p><\/li><li data-start=\"4092\" data-end=\"4149\"><p data-start=\"4094\" data-end=\"4149\">not designed for lock-free reads from the audio thread,<\/p><\/li><li data-start=\"4150\" data-end=\"4197\"><p data-start=\"4152\" data-end=\"4197\">large and complex, with internal allocations.<\/p><\/li><\/ul><p data-start=\"4199\" data-end=\"4305\">If an operator read from your configuration USTRUCT directly, you get two fundamental classes of problems:<\/p><h3 data-start=\"4307\" data-end=\"4336\">A) Lifetime and ownership<\/h3><p data-start=\"4337\" data-end=\"4395\">The operator is running while the editor\/game world might:<\/p><ul data-start=\"4397\" data-end=\"4476\"><li data-start=\"4397\" data-end=\"4426\"><p data-start=\"4399\" data-end=\"4426\">reload\/modify the document,<\/p><\/li><li data-start=\"4427\" data-end=\"4447\"><p data-start=\"4429\" data-end=\"4447\">rebuild the graph,<\/p><\/li><li data-start=\"4448\" data-end=\"4476\"><p data-start=\"4450\" data-end=\"4476\">destroy objects due to GC.<\/p><\/li><\/ul><p data-start=\"4478\" data-end=\"4609\">Even if <em data-start=\"4486\" data-end=\"4497\">you think<\/em> it won\u2019t happen, MetaSound\u2019s pipeline is explicitly designed so the operator doesn\u2019t depend on those lifetimes.<\/p><h3 data-start=\"4611\" data-end=\"4642\">B) Threading and data races<\/h3><p data-start=\"4643\" data-end=\"4723\">Even if lifetime was stable, direct access introduces cross-thread reads\/writes:<\/p><ul data-start=\"4725\" data-end=\"4854\"><li data-start=\"4725\" data-end=\"4802\"><p data-start=\"4727\" data-end=\"4802\">Game thread modifies configuration (e.g., user changes Details panel value)<\/p><\/li><li data-start=\"4803\" data-end=\"4854\"><p data-start=\"4805\" data-end=\"4854\">Audio thread reads the configuration concurrently<\/p><\/li><\/ul><p data-start=\"4856\" data-end=\"5066\">For simple POD types you can sometimes make that safe with atomics, but for types like <code data-start=\"4943\" data-end=\"4952\">FString<\/code>, <code data-start=\"4954\" data-end=\"4962\">TArray<\/code>, and complex structs, it becomes unsafe quickly unless you build a dedicated synchronization mechanism.<\/p><p data-start=\"5068\" data-end=\"5097\">MetaSound\u2019s design intent is:<\/p><blockquote data-start=\"5099\" data-end=\"5208\"><p data-start=\"5101\" data-end=\"5208\"><strong data-start=\"5101\" data-end=\"5208\">Game-thread\/Editor data (configuration) should be copied into a runtime payload designed for operators.<\/strong><\/p><\/blockquote><p data-start=\"5210\" data-end=\"5250\"><code data-start=\"5210\" data-end=\"5225\">IOperatorData<\/code> is that runtime payload.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-4ce476c e-flex e-con-boxed e-con e-parent\" data-id=\"4ce476c\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2861937 elementor-widget elementor-widget-heading\" data-id=\"2861937\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">The System in One Sentence<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e431b9e elementor-widget elementor-widget-text-editor\" data-id=\"e431b9e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"5672\" data-end=\"5794\"><strong data-start=\"5672\" data-end=\"5794\">Node Configuration (USTRUCT, serialized)<\/strong><\/p><p data-start=\"5672\" data-end=\"5794\"><strong data-start=\"5672\" data-end=\"5794\"> \u2192 produces Operator Data (<code data-start=\"5741\" data-end=\"5756\">IOperatorData<\/code>, runtime)<\/strong><\/p><p data-start=\"5672\" data-end=\"5794\"><strong data-start=\"5672\" data-end=\"5794\"> \u2192Operatator Data used by Operator (AudioThread DSP).<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-52c8ecb e-flex e-con-boxed e-con e-parent\" data-id=\"52c8ecb\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-be17dd2 elementor-widget elementor-widget-heading\" data-id=\"be17dd2\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Practical Integration (Minimal Example)<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1368cbb elementor-widget elementor-widget-text-editor\" data-id=\"1368cbb\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"5847\" data-end=\"5883\">We\u2019ll build a minimal configuration:<\/p><ul data-start=\"5885\" data-end=\"6070\"><li data-start=\"5885\" data-end=\"5926\"><p data-start=\"5887\" data-end=\"5926\">A string property called <code data-start=\"5912\" data-end=\"5926\">ConfigString<\/code><\/p><\/li><li data-start=\"5927\" data-end=\"5976\"><p data-start=\"5929\" data-end=\"5976\">Stored per node instance in the MetaSound asset<\/p><\/li><li data-start=\"5977\" data-end=\"6007\"><p data-start=\"5979\" data-end=\"6007\">Exposed in the Details panel<\/p><\/li><li data-start=\"6008\" data-end=\"6070\"><p data-start=\"6010\" data-end=\"6070\">Copied into an <code data-start=\"6025\" data-end=\"6040\">IOperatorData<\/code> type for operator consumption<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-0249cee e-flex e-con-boxed e-con e-parent\" data-id=\"0249cee\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-99d7753 elementor-widget elementor-widget-heading\" data-id=\"99d7753\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">1. Define an Operator Data type<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5457417 elementor-widget elementor-widget-text-editor\" data-id=\"5457417\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"5847\" data-end=\"5883\">MetaSound provides <code data-start=\"6128\" data-end=\"6146\">TOperatorData&lt;T&gt;<\/code> which implements <code data-start=\"6164\" data-end=\"6179\">IOperatorData<\/code> and type naming. You must provide a unique <code data-start=\"6223\" data-end=\"6245\">OperatorDataTypeName<\/code>.\u00a0 The name has to be exactly <code data-start=\"6223\" data-end=\"6245\">OperatorDataTypeName<\/code> or the template won&#8217;t compile. Other than that, you can add whatever you want to your &#8220;OperatorDataType&#8221;, the lame copy of the ConfigData.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-918229d elementor-widget elementor-widget-code-highlight\" data-id=\"918229d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>namespace Metasound\n{\n\tclass FExampleOperatorData final : public TOperatorData<FExampleOperatorData>\n\t{\n\tpublic:\n\t\tstatic const FLazyName OperatorDataTypeName;\n\n\t\texplicit FExampleOperatorData(const FString& InConfigString)\n\t\t\t: ConfigString(InConfigString)\n\t\t{\n\t\t}\n\n\t\tFString ConfigString;\n\t};\n}\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5c04482 elementor-widget elementor-widget-text-editor\" data-id=\"5c04482\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"6367\" data-end=\"6379\">In the <code data-start=\"6372\" data-end=\"6378\">.cpp<\/code>:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9d4dbd9 elementor-widget elementor-widget-code-highlight\" data-id=\"9d4dbd9\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>const FLazyName Metasound::FExampleOperatorData::OperatorDataTypeName =\n\tTEXT(\"ExampleOperatorData\");\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2537eb4 elementor-widget elementor-widget-text-editor\" data-id=\"2537eb4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h3 data-start=\"6495\" data-end=\"6514\">Why this exists<\/h3><p data-start=\"6515\" data-end=\"6631\">This object is what the operator is allowed to \u201cown\u201d and read during DSP. It is not a USTRUCT; it is not GC-managed.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-6551545 e-flex e-con-boxed e-con e-parent\" data-id=\"6551545\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-009e19c elementor-widget elementor-widget-heading\" data-id=\"009e19c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">2 Create the Node Configuration USTRUCT<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e894194 elementor-widget elementor-widget-code-highlight\" data-id=\"e894194\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>USTRUCT()\nstruct FExampleNodeConfiguration : public FMetaSoundFrontendNodeConfiguration\n{\n\tGENERATED_BODY()\n\n\tUPROPERTY(EditAnywhere, Category=\"General\")\n\tFString ConfigString;\n\n\tvirtual TSharedPtr<const Metasound::IOperatorData> GetOperatorData() const override;\n\nprivate:\n\tmutable TSharedPtr<Metasound::FExampleOperatorData> OperatorData;\n};\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e070577 elementor-widget elementor-widget-text-editor\" data-id=\"e070577\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"6367\" data-end=\"6379\">In the <code data-start=\"6372\" data-end=\"6378\">.cpp<\/code>:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c1e1a2d elementor-widget elementor-widget-code-highlight\" data-id=\"c1e1a2d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>TSharedPtr<const Metasound::IOperatorData>\nFExampleNodeConfiguration::GetOperatorData() const\n{\n\tif (!OperatorData.IsValid())\n\t{\n\t\tOperatorData = MakeShared<Metasound::FExampleOperatorData>(ConfigString);\n\t}\n\telse\n\t{\n\t\t\/\/ Updates are reflected through the same shared object.\n\t\tOperatorData->ConfigString = ConfigString;\n\t}\n\n\treturn OperatorData;\n}\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ef8212d elementor-widget elementor-widget-text-editor\" data-id=\"ef8212d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h3 data-start=\"7423\" data-end=\"7457\">\u00a0GetOperatorData() function override<\/h3><p data-start=\"7458\" data-end=\"7641\">You can see that we&#8217;re basically creating an instance of our IOperatorData (FExampleOperatorData) and we simply copy our class members (ConfigString) to\u00a0 it. This is where we&#8217;re copying GT data to the AT owned object.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-8ebdf22 e-flex e-con-boxed e-con e-parent\" data-id=\"8ebdf22\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4ba3168 elementor-widget elementor-widget-heading\" data-id=\"4ba3168\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">3. Update your FNodeFacade class<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ea1a877 elementor-widget elementor-widget-text-editor\" data-id=\"ea1a877\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"7423\" data-end=\"7457\">You&#8217;ll then need to update (replace) your FNodeFacade class. The reason being your FNodeFacade class needs a specific constructor in order for all of this to work. To avoid the headache, you can simply define your node facade like this:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-b11a565 elementor-widget elementor-widget-code-highlight\" data-id=\"b11a565\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>namespace Metasound\n{\n\tusing FExampleNode = TNodeFacade<FExampleOperator>;\n}\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9628ea6 elementor-widget elementor-widget-text-editor\" data-id=\"9628ea6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h3 data-start=\"8338\" data-end=\"8356\">Why this works<\/h3><p data-start=\"8357\" data-end=\"8554\"><code data-start=\"8357\" data-end=\"8370\">TNodeFacade<\/code> provides the constructors and wiring expected by the MetaSound node registry so that the node instance can carry configuration-produced operator data into the operator build pipeline.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-a8d983b e-flex e-con-boxed e-con e-parent\" data-id=\"a8d983b\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b736499 elementor-widget elementor-widget-heading\" data-id=\"b736499\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">4. Update The Register Node Macro To Include The Configuration<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-170f900 elementor-widget elementor-widget-text-editor\" data-id=\"170f900\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"8597\" data-end=\"8630\">Replace your old Register Node macro with this<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7806982 elementor-widget elementor-widget-code-highlight\" data-id=\"7806982\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>METASOUND_REGISTER_NODE_AND_CONFIGURATION(\n\tMetasound::FExampleNode,\n\tFExampleNodeConfiguration\n);\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-bb32634 elementor-widget elementor-widget-text-editor\" data-id=\"bb32634\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h3 data-start=\"8743\" data-end=\"8763\">Why this matters<\/h3><p data-start=\"8764\" data-end=\"8817\">Without this pairing, the node registry doesn\u2019t know:<\/p><ul data-start=\"8819\" data-end=\"9006\"><li data-start=\"8819\" data-end=\"8876\"><p data-start=\"8821\" data-end=\"8876\">which configuration struct belongs to your node class<\/p><\/li><li data-start=\"8877\" data-end=\"8926\"><p data-start=\"8879\" data-end=\"8926\">how to instantiate\/store it per node instance<\/p><\/li><li data-start=\"8927\" data-end=\"8965\"><p data-start=\"8929\" data-end=\"8965\">how to expose it in editor details<\/p><\/li><li data-start=\"8966\" data-end=\"9006\"><p data-start=\"8968\" data-end=\"9006\">how to produce operator data from it<\/p><\/li><\/ul>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-2f789a0 e-flex e-con-boxed e-con e-parent\" data-id=\"2f789a0\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4343ed1 elementor-widget elementor-widget-heading\" data-id=\"4343ed1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">5. Use The Operator Data (config data) in your Operator<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-48a9fb6 elementor-widget elementor-widget-text-editor\" data-id=\"48a9fb6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"8597\" data-end=\"8630\">Your operator has a CreateOperator() static member that you&#8217;ve been using. It&#8217;s in this function where we&#8217;ll grab the serialized data from <strong>InParams.Node.GetOperatorData()<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6ac512d elementor-widget elementor-widget-code-highlight\" data-id=\"6ac512d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>TUniquePtr<IOperator> FExampleOperator::CreateOperator(\n\tconst FBuildOperatorParams& InParams,\n\tFBuildResults& OutResults)\n{\n\tconst TSharedPtr<const IOperatorData> NodeData =\n\t\tInParams.Node.GetOperatorData();\n\n\tconst FExampleOperatorData* Typed =\n\t\tCastOperatorData<const FExampleOperatorData>(NodeData.Get());\n\n\tif (!Typed)\n\t{\n\t\treturn nullptr;\n\t}\n\n\tconst TSharedPtr<const FExampleOperatorData> TypedShared =\n\t\tStaticCastSharedPtr<const FExampleOperatorData>(NodeData);\n\n\treturn MakeUnique<FExampleOperator>(TypedShared, InParams);\n}\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4c81575 elementor-widget elementor-widget-text-editor\" data-id=\"4c81575\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h2 data-start=\"9728\" data-end=\"9746\">Add the OperatorData As Constructor Argument and Store it.<\/h2><p data-start=\"8968\" data-end=\"9006\">Make sure your Operator class&#8217;s constructor has the OperatorData as an In Member so that you can store it.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-356fd5a elementor-widget elementor-widget-code-highlight\" data-id=\"356fd5a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>class FExampleOperator : public TExecutableOperator<FExampleOperator>\n{\nprivate:\n\tTSharedPtr<const FExampleOperatorData> OperatorData;\n\n\/\/Constructor here()...etc\n};\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-d11e686 e-flex e-con-boxed e-con e-parent\" data-id=\"d11e686\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-baf6011 elementor-widget elementor-widget-heading\" data-id=\"baf6011\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">NodeConfiguration's OverrideDefaultInterface() function<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-38f5bf6 elementor-widget elementor-widget-text-editor\" data-id=\"38f5bf6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"11212\" data-end=\"11261\">Node Configuration has a second major capability:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-b057f55 elementor-widget elementor-widget-code-highlight\" data-id=\"b057f55\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-cpp line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-cpp\">\n\t\t\t\t\t<xmp>virtual TInstancedStruct<FMetasoundFrontendClassInterface>\nOverrideDefaultInterface(const FMetasoundFrontendClass& InNodeClass) const;\n<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-664d92d elementor-widget elementor-widget-text-editor\" data-id=\"664d92d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p data-start=\"11410\" data-end=\"11457\">This is not about passing data to the operator.<\/p><p data-start=\"11459\" data-end=\"11531\">This is about changing the <strong data-start=\"11486\" data-end=\"11507\">node\u2019s pin schema<\/strong> based on configuration.<\/p><p data-start=\"11533\" data-end=\"11587\">Use it when configuration affects wiring, for example:<\/p><ul data-start=\"11589\" data-end=\"11744\"><li data-start=\"11589\" data-end=\"11626\"><p data-start=\"11591\" data-end=\"11626\">variable number of inputs\/outputs<\/p><\/li><li data-start=\"11627\" data-end=\"11654\"><p data-start=\"11629\" data-end=\"11654\">optional sidechain pins<\/p><\/li><li data-start=\"11655\" data-end=\"11693\"><p data-start=\"11657\" data-end=\"11693\">mode switches that add\/remove pins<\/p><\/li><li data-start=\"11694\" data-end=\"11744\"><p data-start=\"11696\" data-end=\"11744\">channel count selection that changes IO layout<\/p><\/li><\/ul><p data-start=\"11746\" data-end=\"11839\">It operates at the graph schema level rather than runtime DSP and deserves its own article.<\/p><blockquote data-start=\"11841\" data-end=\"11952\"><p data-start=\"11843\" data-end=\"11952\"><code data-start=\"11843\" data-end=\"11862\">GetOperatorData()<\/code> controls operator behavior.<br data-start=\"11890\" data-end=\"11893\" \/><code data-start=\"11895\" data-end=\"11923\">OverrideDefaultInterface()<\/code> controls the node interface.<\/p><\/blockquote>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"featured_media":0,"template":"","asx-lms-tutorial-categories":[116,45],"asx-lms-tutorial-tags":[46],"class_list":["post-12570","asx-lms-tutorial-cpt","type-asx-lms-tutorial-cpt","status-publish","hentry","asx-lms-tutorial-categories-metasounds-c","asx-lms-tutorial-categories-unreal-engine","asx-lms-tutorial-tags-cpp","entry"],"acf":[],"_links":{"self":[{"href":"https:\/\/store.algosyntax.com\/asx-rest\/wp\/v2\/asx-lms-tutorial-cpt\/12570","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/store.algosyntax.com\/asx-rest\/wp\/v2\/asx-lms-tutorial-cpt"}],"about":[{"href":"https:\/\/store.algosyntax.com\/asx-rest\/wp\/v2\/types\/asx-lms-tutorial-cpt"}],"version-history":[{"count":2,"href":"https:\/\/store.algosyntax.com\/asx-rest\/wp\/v2\/asx-lms-tutorial-cpt\/12570\/revisions"}],"predecessor-version":[{"id":12639,"href":"https:\/\/store.algosyntax.com\/asx-rest\/wp\/v2\/asx-lms-tutorial-cpt\/12570\/revisions\/12639"}],"wp:attachment":[{"href":"https:\/\/store.algosyntax.com\/asx-rest\/wp\/v2\/media?parent=12570"}],"wp:term":[{"taxonomy":"asx-lms-tutorial-categories","embeddable":true,"href":"https:\/\/store.algosyntax.com\/asx-rest\/wp\/v2\/asx-lms-tutorial-categories?post=12570"},{"taxonomy":"asx-lms-tutorial-tags","embeddable":true,"href":"https:\/\/store.algosyntax.com\/asx-rest\/wp\/v2\/asx-lms-tutorial-tags?post=12570"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}