[rai scribble docs tom@zwizwa.be**20130529155037 Ignore-this: b03719f203fe82c12929c2b63c1a77bd ] move ./rai/doc/rai.html ./rai/doc/rai_merge_later.html hunk ./meta.txt 29999 --load/-store doesn't work. Time to think a bit about it. +-load/-store inside a loop doesn't work. Time to think a bit about +it. hunk ./meta.txt 30015 + +Entry: Stream vs. param +Date: Wed May 29 08:50:46 EDT 2013 + +What about making inputs referred to in a `hold' automatically into a +param? + + +Entry: Time feedback +Date: Wed May 29 08:51:49 EDT 2013 + +Still doesn't sit well. Doing stuff behind the scenes seems +unnatural. While composition of recursive systems is nice, it is not +clear where the state goes. So having an explicit "capture" for the +state feedback might be a better approach. + +Making the time loop explicit will also remove the awkward "hold" and +"setup" constructs. But it will break stream semantics. + +Essentially, what is needed is some construct that will transform what +we have now into an explicit loop form like this: + +(loop (t (t_endx 64)) + ((acc ...)) + ((in ...)) + body) + +Where all the acc stuff is threaded. + +The threading really is an essential feature, but it has an intrinsic +conflict: can't get at that which is hidden! + +So, the stream semantics is essential. + + +Entry: RAI doc merge +Date: Wed May 29 09:16:06 EDT 2013 + + + + + + + RAI + + + + + + + + + + + + + +
+

RAI

+ Introduction +
+ +
+ +

+
+

+ + +
    + +
  • Go over FeldSpar and its goals and inspirations. +This paper has a nice list of references to Spiral, Pan, Obsidian, + + +
  • Compared to existing systems, RAI is probably most similar +to Faust, a functional stream +processor language developed at Grame in France. The main difference +with Faust are: +
      +
    • RAI is an extention to an established programming language, +i.e. Racket, a Scheme +dialect with extensive support for writing embedded DSLs. +
    • A core element of RAI is the ability to easily construct +alternative language interpretations. +
    • RAI supports control rate programming, allowing slow rate update of parameters that are expensive to compute. +
    • Feedback in RAI follows a more traditional approach using a state-space update equation, while faust uses a 2-operand combinator. +
    • On the practical side, Faust is over 10 years old and has a lot of support and libraries. +
    • Faust has a more sophisticated compiler/optimizer. +
    +
+ +
+ +
+
+
+ + + + + + hunk ./rai/Makefile 45 + +%.html: %.scrbl + scribble --html --dest $(dir $@) $< + + addfile ./rai/demo.rkt hunk ./rai/demo.rkt 1 +#lang scheme/base + addfile ./rai/doc/rai.scrbl hunk ./rai/doc/rai.scrbl 1 +#lang scribble/doc +@(require scribble/manual + scribble/eval +) + +@(define-syntax-rule ($ id) (racket id)) + +@title{RAI} + +@section{Introduction} + +Racket Abstract Interpretation (RAI), is a code analysis and +generation system built around a domain specific language (DSL). + +The DSL is a purely functional dataflow language aimed at +describing digital signal processing (DSP) algorithms, and is embedded +in the @hyperlink["http://racket-lang.org" "Racket"] programming +language as @codeblock|{#lang s-exp (planet zwizwa/rai/stream)}| + +The system allows for multiple abstract interpretations (AI) of +programs expressed in the DSL. Some interpretations included are + +@itemlist[ + +@item{Scheme code for interactive exploration} + +@item{Dependency-free C code to run on small target systems} + +@item{linear(ized) transfer function for frequency analysis} + +@item{Automatic Differentiation} + +@item{Racklog program for logic analysis} + +@item{Symbolic mathematical expressions} + +] + +Adding such interpretations is straightforward. The core language is +represented as abstract syntax, parameterized by a structure +describing the implementation of the language primitives. Writing a +new interpretation involves the execution of a program parameterized +by such a description, often in some form of context. + +Program interpretations can be constructed to be composable, i.e. an +iterpretation can transform a RAI program into another RAI program. + + + +@section{Time and Space} + +The language consists mostly of two parts, reflecting the qualitative +difference of time and space dimensions. + +@itemlist[ + +@item{An arithmetic core that can be given both a scalar and a stream +semantics, in that basic operators operate on scalar values or +infinite stream values, possibly collected in finite, nested +@emph{spatial} arrays.} + +@item{A a collection of primitives that allows the creation of +@emph{causal temporal} relations, requiring a stream interpretation of +the input/output data.} + +] + +The design of the temporal primitives is motivated by applications from +the domain of musical signal processing (music DSP). Such applications + +@itemlist[ + +@item{rely heavily on the composition of output feedback systems such +as linear IIR filters and other state machines, and} + +@item{often use a parameter update scheme that executes at a lower +sampling rate, i.e. the @emph{control rate}.} ] + +The semantics of the full stream processing language is quite close to +that of @hyperlink["http://faust.grame.fr" "Faust"]. + + +@section{Memory--less Operations} + +@itemlist[ +@item{Arithmetic operations: +@$[+] +@$[-] +@$[*] +@$[/] +@$[sin] +@$[cos] +@$[exp] +@$[log] +... +} +@item{Finite array traversal with accumulator and array output: @$[loop]} +@item{Scheme language forms: +@$[define] +@$[lambda] +@$[let] +@$[let*] +@$[let-values] +@$[let*-values] +@$[values] +... +} +@item{Standard Scheme forms for @emph{unrolled} Metaprogramming} +] + +@section{Causal Stream Operations} + +@itemlist[ +@item{Unit delay output-feedback form: @$[feedback]} +@item{Control-rate operators: @$[hold] @$[setup]} +@item{Support for variable length delay lines.} +] + + +@section{Unrolled Metaprogramming} + +In some cases it is desirable to specify an algorithm at a higher +level using data structures and associated operations that are not +supported on a target platform. E.g. in RAI the C target only +supports operations on (nested) linear arrays, which is a +straightforward translation of the @$[loop] construct. + +In this case, ideally one would want to relate the higher level data +structures and manipulations to some optimized combination of loops +over arrays. A good example of this is the way in which loop fusion +is performed in the +@hyperlink["http://dsl4dsp.inf.elte.hu/feldspar/index.html" +"Feldspar"] language. Such an approach requires a certain amount of +trickery to be implemented. + +There is another way to perform powerful code generation when one +drops to manipulation of scalar values, which relate to variable nodes +in a generated program. This approach could be called the generation +of @emph{flat} or @emph{unrolled} code. + +The RAI language contains primitives for array construction and +deconstruction that allows this form of flexible code generation to be +combined with the C target's array datastructures. The automatic +array unpacking is performed in the @$[map] operator. + + +Keep in mind that while this can lead to efficient implementations if +the unrolling exposes optimization opportunities, in general the +unrolling can be quite inefficient concerning code size if it is +applied to large collections of data. + + + +@section{Examples} +@defmodule[(planet zwizwa/rai/demo)] + + hunk ./rai/doc/rai_merge_later.html 1 - - - - - - RAI - - - - - - - - - - - - - -
-

RAI

- Introduction -
- -
- -

-
-

- - -
    - -
  • Go over FeldSpar and its goals and inspirations. -This paper has a nice list of references to Spiral, Pan, Obsidian, - - -
  • Compared to existing systems, RAI is probably most similar -to Faust, a functional stream -processor language developed at Grame in France. The main difference -with Faust are: -
      -
    • RAI is an extention to an established programming language, -i.e. Racket, a Scheme -dialect with extensive support for writing embedded DSLs. -
    • A core element of RAI is the ability to easily construct -alternative language interpretations. -
    • RAI supports control rate programming, allowing slow rate update of parameters that are expensive to compute. -
    • Feedback in RAI follows a more traditional approach using a state-space update equation, while faust uses a 2-operand combinator. -
    • On the practical side, Faust is over 10 years old and has a lot of support and libraries. -
    • Faust has a more sophisticated compiler/optimizer. -
    -
- -
- -
-
-
- - - rmfile ./rai/doc/rai_merge_later.html addfile ./rai/doc/scribble-common.js hunk ./rai/doc/scribble-common.js 1 +// Common functionality for PLT documentation pages + +// Page Parameters ------------------------------------------------------------ + +var page_query_string = + (location.href.search(/\?([^#]+)(?:#|$)/) >= 0) && RegExp.$1; + +var page_args = + ((function(){ + if (!page_query_string) return []; + var args = page_query_string.split(/[&;]/); + for (var i=0; i= 0) args[i] = [a.substring(0,p), a.substring(p+1)]; + else args[i] = [a, false]; + } + return args; + })()); + +function GetPageArg(key, def) { + for (var i=0; i= 0 && cur.substring(0,eql) == key) + return unescape(cur.substring(eql+1)); + } + return def; +} + +function SetCookie(key, val) { + var d = new Date(); + d.setTime(d.getTime()+(365*24*60*60*1000)); + try { + document.cookie = + key + "=" + escape(val) + "; expires="+ d.toGMTString() + "; path=/"; + } catch (e) {} +} + +// note that this always stores a directory name, ending with a "/" +function SetPLTRoot(ver, relative) { + var root = location.protocol + "//" + location.host + + NormalizePath(location.pathname.replace(/[^\/]*$/, relative)); + SetCookie("PLT_Root."+ver, root); +} + +// adding index.html works because of the above +function GotoPLTRoot(ver, relative) { + var u = GetCookie("PLT_Root."+ver, null); + if (u == null) return true; // no cookie: use plain up link + // the relative path is optional, default goes to the toplevel start page + if (!relative) relative = "index.html"; + location = u + relative; + return false; +} + +// Utilities ------------------------------------------------------------------ + +var normalize_rxs = [/\/\/+/g, /\/\.(\/|$)/, /\/[^\/]*\/\.\.(\/|$)/]; +function NormalizePath(path) { + var tmp, i; + for (i = 0; i < normalize_rxs.length; i++) + while ((tmp = path.replace(normalize_rxs[i], "/")) != path) path = tmp; + return path; +} + +// `noscript' is problematic in some browsers (always renders as a +// block), use this hack instead (does not always work!) +// document.write(""); + +// Interactions --------------------------------------------------------------- + +function DoSearchKey(event, field, ver, top_path) { + var val = field.value; + if (event && event.keyCode == 13) { + var u = GetCookie("PLT_Root."+ver, null); + if (u == null) u = top_path; // default: go to the top path + u += "search/index.html?q=" + escape(val); + if (page_query_string) u += "&" + page_query_string; + location = u; + return false; + } + return true; +} + +function TocviewToggle(glyph, id) { + var s = document.getElementById(id).style; + var expand = s.display == "none"; + s.display = expand ? "block" : "none"; + glyph.innerHTML = expand ? "▼" : "►"; +} + +// Page Init ------------------------------------------------------------------ + +// Note: could make a function that inspects and uses window.onload to chain to +// a previous one, but this file needs to be required first anyway, since it +// contains utilities for all other files. +var on_load_funcs = []; +function AddOnLoad(fun) { on_load_funcs.push(fun); } +window.onload = function() { + for (var i=0; i + .techinside doesn't work with IE, so use both (and IE doesn't + work with inherit in the second one, so use blue directly) */ +.techinside { color: black; } +.techinside:hover { color: blue; } +.techoutside:hover>.techinside { color: inherit; } + +.SCentered { + text-align: center; +} + +.imageleft { + float: left; + margin-right: 0.3em; +} + +.Smaller{ + font-size: 82%; +} + +.Larger{ + font-size: 122%; +} + +/* A hack, inserted to break some Scheme ids: */ +.mywbr { + width: 0; + font-size: 1px; +} + +.compact li p { + margin: 0em; + padding: 0em; +} + +.noborder img { + border: 0; +} + +.SAuthorListBox { + position: relative; + float: right; + left: 2em; + top: -2.5em; + height: 0em; + width: 13em; + margin: 0em -13em 0em 0em; +} +.SAuthorList { + font-size: 82%; +} +.SAuthorList:before { + content: "by "; +} +.author { + display: inline; + white-space: nowrap; +} + +/* print styles : hide the navigation elements */ +@media print { + .tocset, + .navsettop, + .navsetbottom { display: none; } + .maincolumn { + width: auto; + margin-right: 13em; + margin-left: 0; + } +} hunk ./rai/rai.h 1 -#ifndef _RAI_H_ -#define _RAI_H_ - -#include -#include // host side depends on math.h, but prim.h does not. -#include - -#define PROC_CMD_INFO -1 - -typedef unsigned long long u64; -typedef unsigned int u32; -typedef unsigned char u8; -typedef unsigned long word_t; // pointer-sized int -#define CT_ASSERT(name,expr) typedef int _assert_##name[-(!(expr))] - -// Used for run-time loading -#define RAI_MAGIC_SIZE 16 -#define RAI_MAGIC "rai/x86-64/1 " - -#define RAI_VERSION_SIZE 16 - - -/* The main approach for metadata is to keep the data dumb and - non-reduntant. Interpretation of the format can be abstracted in a - library. - - rai_info is structured data, where all pointers (to strings and - substructures) are represented by a u64 file offset. -*/ -typedef void (*rai_info_run)(float *state, - float **in, - float *param, - float **out, - float *store, - int nb_samples); -struct rai_info_param; -struct rai_info_control; - -struct rai_info { - u8 magic[RAI_MAGIC_SIZE]; - u8 version[RAI_VERSION_SIZE]; - rai_info_run entry; - - /* Node type info. */ - struct rai_info_param *info_state; - struct rai_info_param *info_in; - struct rai_info_param *info_param; - struct rai_info_param *info_out; - struct rai_info_param *info_store; - - /* param UI info. */ - struct rai_info_control *info_control; - - /* Misc info */ - u32 build_stamp; - u32 __reserved; - -} __attribute__((__packed__)); - - -/* param meta info */ -enum rai_scale { - rai_scale_lin = 0, // s0 + (s1 - s1) * v - rai_scale_log = 1, // s1 * (s1 / s1) ^ v - rai_scale_slog = 2, // s2 * (v/(1-v)) ^ s2 "squeezed log / stretched exp" -}; -struct rai_info_control { - int index; - const char *desc; - const char *unit; - enum rai_scale scale; - float s0; // minimum | center - float s1; // maximum | exponent - float range; -} __attribute__((__packed__)); - -struct rai_info_param { - char *name; - u32 *dims; -} __attribute__((__packed__)); - -struct rai_info_preset { - u8 magic[RAI_MAGIC_SIZE]; - u32 header_bytes; - u32 timestamp; - u32 payload_bytes; -} __attribute__((__packed__)); - -/* Maps v \in [0,1] to the control parameter's user feedback scale. */ -static inline float rai_info_control_interpolate(const struct rai_info_control *p, float v) { - float out_v; - switch(p->scale) { - case rai_scale_lin: out_v = p->s0 + (p->s1 - p->s0) * v; break; - case rai_scale_log: out_v = p->s0 * pow(p->s1 / p->s0, v); break; - case rai_scale_slog: out_v = p->s0 * pow(v / (1-v), p->s1); break; - default: out_v = v; break; - } - return out_v; -} - - -/* Load .sp class */ -struct rai_info *rai_load_bin(const char *filename); - -/* Allocation size in nb floats */ -int rai_info_size(const struct rai_info_param *rp); - -/* Parameter offset and dimensions. */ -bool rai_info_find(const struct rai_info_param *pi, - const char *name, int *offset, u32 **dims); - -/* Create proc instance. */ -struct rai_proc { - const struct rai_info *info; - float *state; - float *param; - float *store; -}; -static inline void rai_proc_run(struct rai_proc *p, float **in, float **out, int n) { - p->info->entry(p->state, in, p->param, out, p->store, n); -} -struct rai_proc *rai_proc_new(const struct rai_info *info, - const struct rai_proc *proto); -void rai_proc_free(struct rai_proc *p); - -void rai_proc_preset_save(const struct rai_proc *p, const char *filename); -void rai_proc_preset_load(const struct rai_proc *p, const char *filename, int index); - - -#ifndef PROC -#define PROC(x) proc_##x -#endif - -#define PROC_NB_EL(x,t) ((sizeof(x) / sizeof(t))) -#define PROC_PARAM_DIM(name) PROC_NB_EL(((struct PROC(param) *)0)->name, float) - -#define proc_size_param PROC_NB_EL(struct proc_param, float) -#define proc_size_in PROC_NB_EL(struct proc_in, float*) -#define proc_size_out PROC_NB_EL(struct proc_out, float*) -#define proc_size_state PROC_NB_EL(struct proc_si, float) -#define proc_size_store PROC_NB_EL(struct proc_store, float) - -/* Synth stuff */ -struct rai_voice { - int nb; - float *gate; - float *freq; - int next; -}; -/* Simple round-robin voice allocator. - Some extensions: - - "pedal" action to set the decay of all voices - - voice stealing based on envelope decay -*/ - -static inline void rai_voice_init(struct rai_voice *v, int nb, float *gate, float *freq) { - v->nb = nb; - v->gate = gate; - v->freq = freq; - v->next = 0; -} - -static inline void rai_voice_on(struct rai_voice *v, float freq) { - v->gate[v->next] = 1; - v->freq[v->next] = freq; - v->next = (v->next + 1) % v->nb; -} -static inline void rai_voice_off(struct rai_voice *v, float freq) { - /* Turn off 0 or 1 notes, start from oldest. */ - for (int i=1; i<=v->nb; i++) { - int j = (v->nb + v->next - i) % v->nb; - if (v->freq[j] == freq) { - v->gate[j] = 0; - break; - } - } -} -static inline float rai_midi_to_freq(int midi) { - // 69 -> 440 - float fmidi = midi-69; - return 440 * pow(2, fmidi/12); -} - - - - -/* DEBUG */ -typedef void (*rai_log)(const char *msg, ...); -void rai_print_info(const struct rai_info *ri, rai_log log); - -#endif - - rmfile ./rai/rai.h hunk ./rai/rai.html 1 - - - rmfile ./rai/rai.html hunk ./rai/stream-syntax.rkt 144 +(define-syntax ai-feedback + (syntax-rules () + ((_ (s0 ...) fn) + (ai-app ai-feedback/n + '((#f . s0) ...) + #f ;; get arity from fn + #f ;; get arity + fn)))) + +