Friday 30 August 2013

The world's smallest spreadsheet program

Mr. Carl Sassenrath is always a genius in coding, look this spreadsheet program:




the source code is only 70 lines!!!! He probably wrote it in 2 minutes...

Here is the source:

REBOL [
    Title: "Rebocalc"
    Date: 19-Jun-2001
    Version: 1.0.0
    File: %rebocalc.r
    Author: "Carl Sassenrath"
    Purpose: {The world's smallest spreadsheet program, but very powerful.}
    Email: carl@rebol.com
]
csize: 100x20
max-x: 8
max-y: 16
pane: []
xy: csize / 2 + 1 * 1x0
yx: csize + 1 * 0x1
layout [
    cell:   field csize edge none [enter face   compute   face/para/scroll: 0x0]
    label: text csize white black bold center
]
;--Headings:
char: #"A"
repeat x max-x [
    append pane make label [offset: xy text: char]
    set in last pane 'offset xy
    xy: csize + 1 * 1x0 + xy
    char: char + 1
]
repeat y max-y [
    append pane make label [offset: yx text: y size: csize * 1x2 / 2]
    yx: csize + 1 * 0x1 + yx
]
xy: csize * 1x2 / 2 + 1
;--Cells:
cells: tail pane
repeat y max-y [
    char: #"A"
    repeat x max-x [
        v: to-word join char y
        set v none
        char: char + 1
        append pane make cell [offset: xy text: none var: v formula: none]
        xy: csize + 1 * 1x0 + xy
    ]
    xy: csize * 1x2 / 2 + 1 + (xy * 0x1)
]
enter: func [face /local data] [
    if empty? face/text [exit]
    set face/var face/text
    data: either face/text/1 = #"=" [next face/text][face/text]
    if error? try [data: load data] [exit]
    if find [integer! decimal! money! time! date! tuple! pair!] type?/word :data [set face/var data exit]
    if face/text/1 = #"=" [face/formula: :data]   ; string case falls thru
]
compute: has [blk] [
    unfocus
    foreach cell cells [
        if cell/formula [ ;probe cell/var
            if error? cell/text: try [do cell/formula] [cell/text: "ERROR!"]
            set cell/var cell/text
            show cell
        ]
    ]
]
lo: layout [
    bx: box second span? pane
    pad 55x0
    text as-is trim/auto {
        Cells can be numbers, times, money, tuples, pairs, etc.
        If a cell is not a scalar value, then it is treated as a string.
        Start formulas with the = character.   Any REBOL expression is valid.
        Remember to put spaces between each item in a formula.   Use ( ) where needed.
        Refer to cells as A1 D8 E10.   Example: =A1 + B1 * length? B8
        Example: in A1 type REBOL, in B1 type =length? a1, in C1 type =reverse copy a1
        Then: in D1 =checksum A1.   Now, change A1 to "Amazing!"
        In A2 type 1 + 2 (no =), in B2 type =A2.   Now change A2 to 3 * 4.
        Try: =(now/time) or =request-date or =checksum read rejoin [http://www. A1 ".com"]
        Computation moves from top to bottom. It is non-iterative.
    }

]
bx/pane: pane
view lo

Thursday 29 August 2013

Email client

Today I'll show you a real mail client made in Rebol:

It's extremely fast: fetch from remote server 667 mail in 2 minutes!!!
It contains also the address book!!!
WOW!!!Phil Bevan wrote this great script, impressive!!!

The uncompressed script source is very long, you can download from here: http://www.maxvessi.net/rebsite/readmail.r

Wednesday 21 August 2013

Primitive rebol object browser

The system object is difficult to analyze and study, so there is this simple script to read system:


Here is the source:

REBOL [
    Title: "Primitive Rebol Object Browser"
    Date: 01-nov-2001
    File: %prob.r
    Author: "HY"
    Purpose: "Make a graphical view of the probe function so that users may probe the system object without having to look it all up in the console."
]
path-words: copy []
list-index: 4 ; this should have been 1, but we add 3 for the lay-out components...
main-styles: stylize [
  list: text-list 180x200 feel [
    detect: func [face event] [
      if event/1 = 'up [
        if not equal? "" to-string face/picked [
          ; make sure clicks on the slider are ignored:
          event-offset: event/offset - face/offset
          if event-offset/x >= face/sld/offset/x [ return event ]
          ; if we get here, everything is ok.
          ; Now make sure only one item is selected:
          if (length? face/picked) > 1 [
            ;print "Faen steike! Mer enn én dings er valgt!"
            ; hvorfor funker ikke dette:
            ;face/picked: face/picked/1
            ;print "Det får holde med den første."
            show face
          ]
          list-clicked: index? find face/parent-face/pane face ;list1 = 4
                                                              ;list2 = 5
                                                              ;list3 = 6
                                                              ;list4 = 7
          ;print ""
          ;print ["list-index er" list-index "og list-clicked er" list-clicked]
          while [list-index > list-clicked] [
            remove back tail path-words
            ;if list-index < 8 [
              clear face/parent-face/pane/:list-index/lines
              clear face/parent-face/pane/:list-index/picked
              show face/parent-face/pane/:list-index ; immediately update
            ;]
            list-index: list-index - 1
          ]
          ;print ["list-index er" list-index "og list-clicked er" list-clicked]
          to-append: to-word to-string face/picked/1
          while [all [((length? path-words) > (list-clicked - 3)) list-clicked < 7]] [
            ;print ["(length? path-words) > (list-clicked - 3) er"
            ;         (length? path-words) > (list-clicked - 3)]
            ;print ["length? path-words er" (length? path-words)]
            ;print ["list-clicked er" list-clicked]
            move-lists-right
            list-clicked: min 7 list-clicked + 1
          ]
          append path-words to-append
          path-text/text: to-path path-words
          show path-text
          ; set up right highlight colour:
          for x 4 7 1 [
            face/parent-face/pane/:x/iter/feel/set-high-col either (x = list-clicked) [240.240.50] [130.130.130]
            show face/parent-face/pane/:x
          ]
          ;print ["list-index er" list-index "og list-clicked er" list-clicked]
          list-index: list-clicked + 1 ; list-index is the one we're manipulating,
                                      ; i.e. the one to the right of list-clicked
          ;print "^(1B)[J" ; clear screen ; ] (added end bracket so that textpad may find the right brackets)
          to-display: do compose [(to-path path-words)] ;to-path path-words
          c: do compose [(to-path copy/part path-words (length? path-words) - 1)]
          if unset? get/any in c last path-words [
            pop-up join form to-path path-words ": undefined"
            if list-index = 8 [ list-index: 7 ]
            return
          ]
          if any-function? get in c last path-words [
            cc: get in c last path-words
            pop-up source-string cc
            if list-index = 8 [ list-index: 7 ]
            return
          ]
          if object? get in c last path-words [
            if list-index = 8 [ ; 8 would be the slider bar
              ;print ""
              ;print reduce ["list4/lines er" mold list4/lines]
              move-lists-left
              list4/iter/feel/set-high-col 240.240.50
              list-index: 7
            ]
            ;face/parent-face/pane/:list-index/lines: sort first to-display
            face/parent-face/pane/:list-index/lines: sort first get in c last path-words
            face/parent-face/pane/:list-index/data: sort first get in c last path-words
            clear face/parent-face/pane/:list-index/picked
            update-slider face/parent-face/pane/:list-index
            show face/parent-face/pane/:list-index ; update this view again
            return
          ]
          if port? get in c last path-words [
            pop-up mold get in c last path-words
            if list-index = 8 [ list-index: 7 ]
            return
          ]
          if block? get in c last path-words [
            pop-up mold get in c last path-words
            if list-index = 8 [ list-index: 7 ]
            return
          ]
          pop-up to-string to-display
          if list-index = 8 [list-index: 7 remove back tail path-words ]
        ] ; end face/picked not ""
      ] ; end event/1 = 'up
      return event
    ] ; end detect
  ] ; end list
] ; end main-styles
move-lists-left: func [] [
  ;print "Flytter listene ett hakk til venstre"
  list1/lines: head copy list2/lines
  list1/picked: copy list2/picked
  list1/iter/feel/set-high-col list2/iter/feel/high-col
  show list1
  update-slider list1
  list2/lines: head copy list3/lines
  list2/picked: copy list3/picked
  list2/iter/feel/set-high-col list3/iter/feel/high-col
  show list2
  update-slider list2
  list3/lines: head copy list4/lines
  list3/picked: copy list4/picked
  list3/iter/feel/set-high-col list4/iter/feel/high-col
  show list3
  update-slider list3
  ; don't need to mess with list4, as we do so in feel code itself
]
move-lists-right: func [/local to-remove list1-path] [
  list4/lines: head copy list3/lines
  list4/picked: copy list3/picked
  show list4
  update-slider list4
  list3/lines: head copy list2/lines
  list3/picked: copy list2/picked
  show list3
  update-slider list3
  list2/lines: head copy list1/lines
  list2/picked: copy list1/picked
  show list2
  update-slider list2
  to-remove: (length? path-words) - (list-clicked - 3)
  list1-path: to-path head remove/part at copy path-words ((length? path-words) - (to-remove - 1)) to-remove
  list1/lines: sort first list1-path
  list1/picked: to-block first find head list1/lines first at path-words ((length? path-words) - (to-remove - 1))
  show list1
  update-slider list1
]
update-slider: func [faces [object! block!]] [
  foreach lv-list to-block faces [
    lv-list/sn: 0
    lv-list/sld/data: 0
    lv-list/sld/redrag lv-list/lc / max 1 length? head lv-list/lines
    show lv-list
  ]
]
pop-up: func [to-show /name 'word] [
  l: layout [
    info 300x300 to-show
    button "Close" [hide-popup]
  ]
  inform l
]
source-string: func [
    "Returns the source code for a word, as a string."
    'word [word!]
    /local return-value
][
    ;return-value: join "system/words/" [word ": "]
    return-value: to-string reduce [word ": "]
    if not value? word [append return-value "undefined" return return-value]
    if native? get word [
        append return-value "native"
        append return-value mold third get word
        return return-value
    ]
    if op? get word [
        append return-value "op"
        append return-value mold third get word
        return return-value
    ]
    if action? get word [
        append return-value "action"
        append return-value mold third get word
        return return-value
    ]
    append return-value mold get word
    return return-value
]
PROB: func ['to-be-probed] [
  if not value? 'to-be-probed [to-be-probed: 'system]
  if any [any-function? get to-be-probed port? get to-be-probed] [
    pop-up source-string :to-be-probed
    exit
  ]
  clear path-words
  append path-words to-be-probed
  to-display: do compose [(to-path path-words)] ;to-path path-words
  dom?: attempt [do-browser "true;"]
  link: either dom? ["Try Romano Paolo Tenca's AnaMonitor!"] [""]
  main: layout [
    styles main-styles
    title "Primitive Rebol Object Browser"
    text "Current path: "
    path-text: text form to-path path-words 745x16
    across
    list1: list data sort first to-display
    list2: list
    list3: list
    list4: list
    below
    ;slider1: slider 745x16 [
    ;]
    across
    button "Quit" [quit]
    box 400x1
    text 200 underline blue center link feel [
      over: func [face act pos] [
        face/font/style: either act [[bold]][[underline]]
        show face
      ]
      engage: func [face action event] [
        if action = 'down [ if dom? [ do-browser {top.location.href="http://www.rebol.net/plugin/demos/anamonitor.html";} ] ]
      ]
    ]
  ]
  list1/iter/feel: make list1/iter/feel [
    high-col: 240.240.50 ; actually rebol default colour.
    set-high-col: func [colour] [high-col: colour ]
    redraw: func[f a i] bind [
      f/color: either find picked f/text [high-col] [slf/color]
    ] in list1 'self
  ]
  list2/iter/feel: make list2/iter/feel [
    high-col: 240.240.50
    set-high-col: func [colour] [high-col: colour ]
    redraw: func[f a i] bind [
      f/color: either find picked f/text [high-col] [slf/color]
    ] in list2 'self
  ]
  list3/iter/feel: make list3/iter/feel [
    high-col: 240.240.50
    set-high-col: func [colour] [high-col: colour ]
    redraw: func[f a i] bind [
      f/color: either find picked f/text [high-col] [slf/color]
    ] in list3 'self
  ]
  list4/iter/feel: make list4/iter/feel [
    high-col: 240.240.50
    set-high-col: func [colour] [high-col: colour ]
    redraw: func[f a i] bind [
      f/color: either find picked f/text [high-col] [slf/color]
    ] in list4 'self
  ]
  view main
]
prob system

Monday 19 August 2013

Digital synthesizer PM-101

In 2005 Rebolek (Boleslav Brezovsky) wrote a script to create a digital synthesizer (PM-101), I discovered that script:





I'm not an expert, so I'll copy here the documentation:

1. Introduction


Sintezar PM-101 is Phase Manipulation synthesizer based on Sintezar modular engine.

1.1 What is Phase Manipulation?


Phase manipulation is based on Casio's Phase Modulation used in CZ-synth in 1980's. Phase modulation distorts the speed of reading sinewave and produces different timbres. Phase manipulation adds Phase stretching.

1.2 Getting started


Run the script and press on the intro screen to continue to main program. In the left bottom corner, there's a small toolbar. This is good place to start. Let's look at all the icons in toolbar.

1.2.1 New sound


Sets current parameters to their initial values.

1.2.2 Open sound


Open saved preset.

1.2.3 Save sound (RPS)


Save preset in internal format.

1.2.4 Save sample (WAV)


Save computed sound as WAVE file.

1.2.5 Compute sound


Compute sound from given parameters.

1.2.6 Play sound


Play computed sound.

So open sound, compute it (takes about 10-20sec on 1-2GHz machine) and play it.

2. Architecture of PM-101


2.1 DCO1/DCO2 - Oscillator


There are two Oscillator (OSC) lines in PM-101 that can be combined together to produce sound. Each OSC line consist of Oscillator with 7 different waves. These waves can be combined together in so called Octave Modulation. Waves go to Window function, it's a sort of Amplitude Modulator that can be used together with SineSync wave to produce resonance-like sounds. ou can even shape waves with Window function (try square wave + triangle window).

2.1 Sintezar waves


What waves can be used in Sintezar PM-101? Here's the list:
  • Saw
  • Square
  • Pulse
  • Double sine (this wave is not ready, and squarewave is used instead)
  • Saw-square
  • Triangle
  • Sinesync (use this for resonant waves in combination with window function)

Last 'wave' turns off octave modulation.
Window function uses this waves:
  • No wave (Signal is left intact)
  • Saw
  • Triangle
  • Trapezoid
  • Saw-square

Resulting sound goes to Phase Modulator (DCW). DCW controls shape of wave. With DCW set to zero, wave is sine (cosine) wave, At DCW = 1 wave's got it's original shape. Remember that window function is applied AFTER DCW, so wave's shape is always affected by window function! (Try to set some window function and set DCW to 0. Result is NOT sine!!!)

2.2 DPS - Phase Shaper


Next to DCW is Phase Shaper (DPS). DPS shortens wave, but cycle length is left intact, so it works like a kind of a Pulse Wave Modulator for all waves (it adds higher harmonics, when the pitch remains).

2.3 DCF - Filter


DCF is Digital Controlled Filter. It's simple 2-pole State-Variable filter with cutoff and resonance. You can use basic filter types, or turn filter off

2.3 Filter types

  • Low pass filter
  • High pass filter
  • Band pass filter
  • Notch filter

2.4 DCA - Amplifier


Last module in each line is DCA - Digital Controlled Amplitude. This is normal amplitude envelope.
This module is combined together with main filter. This is same filter as in each osc. line. PM-101 has three 12dB filters together, so by cascading two LP filters it's possible to create fatter 24dB/oct filter, or you can use any other combination of filters.

2.5 FinalCrusher


This module can reduce bit- and sampling- resolution and also implements simple waveshaper which can be used to enhance harmonic spectrum of sound (or to create distortion). You can set waveshaper's threshold using dedicated knob.


Source

Here is the source:
REBOL [
    Title: {Sintezar PM-101 - Phase Manipulation Digital Synthesizer}
    Date: 23-Jun-2006
    Name: 'PM-101
    Version: 0.4.0
    File: %pm-101.r
    Author: "Boleslav Brezovsky"
    Email: "rebolek<>gmail<>com"
    Purpose: "synthetiser"
    History: [
        0.4.0 23-Jun-2006 [
            "FIRST PUBLIC RELEASE"
            {Uses Ladislav's Include to be compatible with preprocessor. Now I can produce single file for easier distribution.}
            "PLAY button crashed when sound buffer was empty."
            "EXPORT-WAV button crashed on cancel"
            "EXPORT button crashed on cancel"
            "IMPORT button crashed on cancel"
            {IMPORT crashed when trying to import non-sintezar file}
        ]
        0.3.9 22-Feb-2006 {OSC basic volume set to -3dB to prevent possible clipping.
^-^-^-^-^-^-^-^-dB equations fixed, maximal gain is up to 12dB}
        0.3.8 19-Sep-2005 {AMPs support +6dB gain, all AMP values shown in dB's.
^-^-^-^-^-^-^-^-FInalCrusher is fully working (freq. divider and bit resolution destroyer)}
        0.3.7 18-Sep-2005 "GUI changes to support LFO's and FinalCrusher"
        0.3.6 16-Sep-2005 {added: waveshaper with normaliser, RingModulator mode 2
^-^-^-^-^-^-^-^-fixed: DCA, resonance}
        0.3.5 15-Sep-2005 {ENV button envelopes are always zoomed at 100%
^-^-^-^-^-^-^-^-!!!ADDED WAV Saver!!! Great moment for Sintezar! ;)}
        0.3.4 14-Sep-2005 {Added: Pitch envelope (+/- 2 oct.), OSC SYNC (Hardsync)}
        0.3.3 13-Sep-2005 {Octave/keyboard for pitch-selection is functional now.
^-^-^-^-^-^-^-^-Vibrato is working now, main DCA fixed.
^-^-^-^-^-^-^-^-SAVE/LOAD support all parameters (I hope ;) - Not all GUI facets are updated after load (knobs)}
        0.3.2 12-Sep-2005 {main filter bug fixed!!! limiter was missing and freq/reso envs were exchanged
^-^-^-^-^-^-^-^-added: Main DCA, init/copy/paste envelope, sample window enhanced}
        0.3.1 9-Sep-2005 {GUI changes, added progress bar, main filter bug still not fixed :/}
        0.3.0 8-Sep-2005 {added: load, GUI update after load. fixed: main filter bug.}
        0.2.5 6-Sep-2005 {save enhanced, envelope rates enhanced (1ms - 16s range exponencial instead of 0s-1s linear),
^-^-^-^-^-^-^-^-envelope refresh fixed (although button envelopes refresh still doesn't work)}
        0.2.4 2-Sep-2005 {added: new mix mode - mix-ring, basic save, intro screen, toolbox, fixed: envelopes, gui changes}
        0.2.3 1-Sep-2005 "added: ESE-eight step envelope, detune part"
        0.2.2 28-Aug-2005 {added: mixer, ring modulator, make/play sound, fixed: envelopes}
        0.2.1 26-Aug-2005 "added: phase stretching"
        0.2.0 19-Aug-2005 {added: filter, more waves, window function, octave modulation}
        0.1.0 15-Aug-2005 {phase distortion rewritten from scratch to make it casioCZ-like}
        0.0.1 25-Jul-2005 "initial version"
    ]
    references: [
        [1] http://en.wikipedia.org/wiki/Phase_distortion_synthesis
        [2] http://homepage.mac.com/synth_seal/html/cz1.html
        [3] http://www.cosmosynthesizer.de/
    ]
    notes: {
CasioCZ waves:
^-sawtooth (done)
^-square (done)
^-pulse (done)
^-double-sine (pending)
^-saw-pulse (done)
^-sine-sync (done)
^-*triangle (not available on CasioCZ, but done)
^-**waveforms are just my aproximation, not checked against original sampled CasioCZ waves
^-
CasioCZ windows:
^-sawtooth (done)
^-triangle (done)
^-trapezoid (done)
^-*saw-pulse (done, unofficial window)
^-*spike (pending, unofficial window)
^-
Octave modulation: CZ can combine two waveforms in a single DCO: DCO can alternatively playback two different waveforms. [2]
When the original wave is constituted by 2 waveforms (octave modulated), the window affects both waves. [2]
^-Thanks to implementation, when two same waveforms are used, window affects both waveforms together, so there's sound difference compared to one waveform.
^-
Envelopes - there is five of them (only ADSR at the moment, I'll write 8-stage ENV later)
^-DCO (pitch) envelope
^-DCW (phase distortion) envelope
^-DCF/cutoff envelope
^-DCF/resonance envelope
^-DCA envelope
Digital phase stretch - not found on CasioCZ - stretch waveform but cycle lenght is left
Mixer/Ring Modulator
^-PH101 supports Mix-Ring - it's ring modulator with second line (modulator) added to output, so it works both as carrier and modulator
}
    missing: [
        #1 'pending {(there are three types of RM on CasioCZ...let's see)}
        #2 'pending {All Casio waves are missing. My waves are different. This is not a priority.}
        #3 'done "vibrato"
        #4 'fixed "there's probably some bug in main filter section"
        #5 'fixed {save does not save global parameters - detune, length (vibrato)}
        #6 'pending "save WAV"
        #7 'pending "strange LED behavior when changing sample length"
        #8 'pending {Math overfolw error where it shouldn't be (square waveh)}
        #9 'pending "Reset phase before making new sound"
        #10 'pending "Hard sync for DCO2"
    ]
]
if not exists? %sounds/ [
    make-dir %sounds/
    path: %sounds/
    verbose: none
    files: [%sounds/bassdrum.sin 392 %sounds/basskiller.sin 425 %sounds/blaum.sin 411 %sounds/cosi.sin 456 %sounds/elecguit.sin 529 %sounds/elecguit2.sin 554 %sounds/elecneguit.sin 453 %sounds/epiano.sin 448 %sounds/hardsync.rps 464 %sounds/horn.sin 475 %sounds/horn.wav 444 %sounds/organ.sin 407 %sounds/pluckpling.sis 416 %sounds/resobass.sin 493 %sounds/ressonator.sin 483 %sounds/trumpet.sin 425 %sounds/violin.sin 448 %sounds/violin2.sin 515 %sounds/wildpitcher.sin 403]
    check: 1799181
    if not exists? path [make-dir path]
    archive: debase {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
zH/gmyQopXgGfCXZ16AUAAA==}

    if check <> checksum archive [print ["Checksum failed" check checksum archive] halt]
    foreach [file len] files [
        if verbose [print [tab file]]
        either len = 'DIR [
            if not exists? file [make-dir/deep file]
        ] [
            data: decompress copy/part as-binary archive len
            archive: skip archive len
            write/binary file data
        ]
    ]
]
toolbox-images: context [
    new-project: first reduce load decompress 64#{
eJztl8EKgzAMhu99CsdeYOhBfAFfYuzgQYYM74Oxdx+zwiJpuqRaU6HJYSDL8v2l
38Cxe/TFMHb3/lRcq/JZlcX5ZdoVlYfzcB6mhy9BZexH1X5bAxvCWwpOG/zI/sS+
2BCec5DkZk6EODcMXwkcgbWZihBTDOpWz5dEug1GiO8zqSS81eLNUhnE2LawsA4l
+REWm3fyeXHaYaXuc9i/d1I+8yMk67M/wrrNCj6Lh8nNzdQH9bkBfTifG9QH8hnD
4wjJ+kzB11Mn7jMFb+r216n67MCGRy+NsK/PDmwK3h9Bw2cHNgc+EZ/9xvrh1X0O
g9d4f3b4LIUn3zE0fOb3H2x/hK3FgHv8ZThfwjVn3hg7D+dh5rB53z5iBFREfxgA
AA==
}

    open-project: first reduce load decompress 64#{
eJztVksKg0AM3ecUll5AlOK+Cy9RunAhpRT3hdK7F80sIjFj5uNnislCUZ55b5KX
tmtebfbsmkd7ym5l8S6L7PyBOiAO8H+B8yG2qBwBnJNYt3IEMNK+1H0CXqRmRK4c
AYxMDe1qSFcJG7aKHn1FUiNh8wmj5PGeSuBvd0Ibg9KjT/j5G2P4WWJUOfcKVWU+
SAbsR3hEm1a41toE/sguhJ4/UA1BtF0lTNDWSwDJBkG0uQRxSDRR1n3OgLkNeDjQ
5hKQBeDFLyfA+G1uAxGsbw8NkL4qMfJsVRB4gra9GXYJM8YIcpXdbTMTZpdgDmxV
2gYstUEjAXgD1qLNG6CXIPpZI2GZCdNIGP0+u5Jf0hh28g5+5hLW8jOX4Onnna8h
u4Qk1hCXkNwawhD9nMQakpqR0BrSS9jtGrJLSGIN8UhuDRnaUhs0Efv/tiftIM0H
OAAM3/sPwwsyNn8YAAA=
}

    save-project: first reduce load decompress 64#{
eJztmDEOwjAMRXefoogLVO0NGHoJxNChQgh1R0LcHQmWhtQ/32kyFNneUH/8X+K/
MI/3qbnN43U6NOe+e/Rdc3zKsKFc7GJQ7aekVeo0pFuYj+JWJ5uZNfMYIdN2IMaT
DU/FI1C2tYNWxHjyEiSxYfgI820HYu0yGARZMlgREpMTS6JZYhAyg6GKeQTBxjCC
OplBqJNn5iAqzxqC59mIsNc859GuTLYiJFJFXVgVMTZP5TlzcjGxahtv0rf64bcl
/onvhBgjZOa5vm2MUCLP1W3HCKXzXN12IMabpFUiz/Vtx8/AIHiey9rGCJ5nI4Ln
2YjgeS5rGyOY8xw8FbNJBWzHCIKNYQTP8/an4hF296fpf4rldXkDbAq9gX8YAAA=
}

    make-sound: first reduce load decompress 64#{
eJztlD1u5DAMhXueYhZ7AVkjS3LpkTSXWGyRIggWi/QBgr178EgWNDRO5J0izfDr
aMl84t/r09/n05/Xp5fnH6dfPrz5cPr5Ttc77HH52y5fGkgOeDbHlj2YHCgNyBmy
h+IK9E8etAncJbtEkB1gEfm6ArKuGkF/KGagT8tgLuC+hB1Nkj1DNkklAH5g+Twm
vyNSdeA/ZY8cWs5gdmBzWSvJVRWXPCRX0P+orSAmQFyd2tguV7AwcQLlAqxHzmhk
Z6wGoEc5J2UFmiSuv9iNN0snzYxrQH6XMpAiLRWkBsi6Ipv8yCpqEfgAVg+0ziop
AHHNVzCiiPYkjSgiaX3X2VRAWoF4fAPbOpu6HS0b2brZyImtV7SRrSKZmsBIw96o
c29chPxF5BHjZs92/qlfAJ+vhDmDTW/zaDUbRz2F2ZOdC7Cd1C/APQ/ZvWm35BTA
ythoSwYhApUdGM+IpQDkR3tLko4mqTK6AOWCjJt8lh7m/tNh8AVIt2vHjjTJ4Q6T
IRVFZw80Ptu8AL08JzBlYH/B2r3OFq9emT/rIVm6N0byDOzvZCS5wHlagcoWkf0T
+AWbOePceXkaSQ6rB+JSD8vo91Z04EbCpEt4hWm0wgxl20oaUUR7kkZsqEn2ykbW
NV62A5Hvkv24/Lj89aF/vz8As15irdINAAA=
}

    play-sound: first reduce load decompress 64#{
eJztlkFuwyAQRfecIlUvANgEWGJjX6LqIouoqqrsK1W9e/pnqISVkIASV6raeavI
Hs3nfyA+7N72m9fD7mX/sHnS/bvuN48fYr6h/mSzUYQHPzu59EAHILPyEUwKrDm5
VJ0EXEKelJ3B7Ij7Ti4255LyGjqQfszADGB9w/QEeHCI4Ewzi2FhKVuqPNvmyUXZ
2hEk7Mq7FB4Huci5Ptsq2UkR0dDM9rBVrItPrYmganKNohvd7oEciYpsF80lSVWT
Sw+cAX4LzAROj62oybakqMEw14PRAxuBMBpsqUYDkjI63OSmLq651aRIpGY5gcEB
q4HzRABKA5aiOzA5cGXNDXvbUXkJ2H4bgJ8BOeVMD1gF5ehEUKBmtQtFdP7PyE6R
BMAT6F/Q0H7xgwVyAN+Xfg/iAOpVLPd2dktuNVBpVSAQVgE3AsEilQTp6NOrUwQN
ky+b1FlA4r5+AMG7NEhQiqSUyI03SWaS6QBbkkdSSmTNbxK2ip2XvHuzRERNJM2T
S3Xmm+RyJHebXGxO94YDNYfkt34y/zev0Cw+n4/dHITu0g0AAA==
}

]
leds: load to block! decompress 64#{
eJzVlr0KwkAQhPt9ihNf4CSCqS3yEmKRIojI9YL47oraxFxmdvIH2YNt7ubbWW6L
TfWtCddUX5pNOO2K+yFsHxZHRK+4OPJj+BrjiBjjZHGmstotse3BCbaFr/LET2zP
c0iLzVBV8Wz4GuOIGONkcaay2i2x7cEJtruxvhmKcf8+OBt7hHBUjHADxJnKWrfU
NsdJtv9jfTNUOrLha4wjYoyTxZnK2J7cswcn2O7GrDNUfsoOy73iUbvB92CcvNK0
bKviTGW1W2LbgxNsC18l/POyMzTBPoRx8krTsj3BPqR2S2x7cILt2WboBVK1ya2X
DQAA
}

logo.png: load as-binary decompress 64#{
eJwBHwPg/IlQTkcNChoKAAAADUlIRFIAAABuAAAALQgCAAAAwPXJbgAAAARnQU1B
AACxjwv8YQUAAALWSURBVGhD7Zo7csJADED3uBwiZcqcgAvQ06dOS0tJSUfJpCGa
CIRYfSybDVmwPCoYW2h3n75mKCWvtgTeT98p0wjUjgArbX0zE2sKt/Ot5bKQnE5l
UA6HQjITeLfHTJTN3J4oE2UzAs0M2VF5PBYSXigjNfTO7X1+FpSnuhJlM3f1h/Lt
7TotNDvmIwz1h3K/9wYvYvL1paiBG+jypzdS2+0KiKq8Xp+1fFOXQmSj5HMit/Xx
UUh8Z08eM2k51T6WUdDBD6vVFcR2q6BcLAoIPAJBy+AGSRzcQCI3QHfQDl/oYipR
Xt4+EuU8onJyrfcTHOsGDGpqWZSLQskDQZtQiEEGLyfB7e/aCa4GfFWA+ezJ9Qf3
6iv4KIP9BJfg8wB8EeumvNQmBvrSW/+Akh/Y6qrgCVLDeMFzJsprB0R2FhHeDTFM
KAEhcwdRYirwkHFiHAzictDoQeRFwxAEJgrsp7sEn4ySYNHUtdmczwks4DMIGMci
SHdQhy7kAmqojz5zGFGBopmJFwQ/UX4XtWul5Wo+V/Islr9XDqLkS3BljKCqJuK7
P13qJM8VgiVVXauaIi1Tt4gSpea2RHkTthmVTgvp/JGd4JZXrd8r+YzJuzAvgtJm
1esD1b1boImymWv+DGWzHT6NoUTZzFU2SnyTQ/G7IT6N/47ZbPN9GUqUzfyRKJ8E
ZfBPTHiaoDJXUzFUdrhOZAl1MxHe418cI1YvOpGt0w6CynNHKflLIk408ZitPjue
RYMyKiObIR1pxFrxQVEZ2X2i9BJe5mzlbe7zRDkOpVoQI5WevqgmOH/q5Kbquarj
VedRLVvtrr6v5PyYVjO2Y1ptxzmSPF7kDiGT7JxW1h3KiC+cgJ0QKX6cVs0kUXr/
mQ+mvKwtE9xGRh6R4L1FpcVLFrdZJ3i8s92jqY53rxaV9wCyeqAz1fGB4Q9RRvL6
ZXT0tx3VsXlzkMDLhEVHB/kBkIiuI8JwoEkAAAAASUVORK5CYIJIk2nxHwMAAA==
}

sine.png: load as-binary decompress 64#{
eJzrDPBz5+WS4mJgYOD19HAJAtKiQMzPwQQkWyyEbYAUY3GQuxPDunMyL4EclnRH
X0cGho393H8SWYF8hWSPIF8Ghio1BoaGFgaGX0ChhhcMDKUGDAyvEhgYrGYwMIgX
zNkVCDIo2dPFMcTCf+vkQC4GBQ7nt8/u3ulxasoxVvro3GPuu3rFKptI0ULzlQ+z
hY6H1c9ITHxz0mDGGa73RQ6LPlvPmV7xzP/oZN0NmY9jv6Q6Lr4rOM/vu738tgff
H339+HpP1auL7w98Xr6cd8Ojje1uLg89gNYxeLr6uaxzSmgCAFN4UjvlAAAA
}

triangle.png: load as-binary decompress 64#{
eJzrDPBz5+WS4mJgYOD19HAJAtKiQMzPwQQkWyyEbYAUS7qjryMDw8Z+7j+JrEB+
uqeLY4iF/9LJkXwMihzO1ubfn/F0/mYzlagPiTvaNL9DwURIYGbVfwuFiLI3T0Xm
PlTvPNcvoR/4Q1ZBdU2LJGfGhKf7q1/tqj29aNZbrjlal1xuZt1fdfjTWv1pz20+
HL/WZbq/TuaQnfYT9ikHNnXyvT7lC7SSwdPVz2WdU0ITAKEvP8CwAAAA
}

pulse.png: load as-binary decompress 64#{
eJzrDPBz5+WS4mJgYOD19HAJAtKiQMzPwQQkWyyEbYAUS7qjryMDw8Z+7j+JrEC+
m6eLY4iFf/Kf///tmV0XNDY2Ch1h2CKnz2QlwM+QeUy9N1zszfeF6+XjfzDbdqiX
Odw7Lr+nxSn9x2v1CQ6aXn8OrDgo/48llfF2dNfGhjkZ+4EmMni6+rmsc0poAgBm
VS4QjwAAAA==
}

saw.png: load as-binary decompress 64#{
eJzrDPBz5+WS4mJgYOD19HAJAtKiQMzPwQQkWyyEbYAUS7qjryMDw8Z+7j+JrEB+
tKeLY4iF/9LJnnwMChzMr8//tXxUFNB06lLFYb/GDEWlJSGNZxiPC+/onjHN0sfy
ELPI0krxp1Z7Jl76u/5v/er7W+2lJFVnh3Qdtg9mOfpWfMqC+uVP5OzrZW7/Wrdd
ykJ03tzPz/xcgNYweLr6uaxzSmgCAD2dOcakAAAA
}

keyboard.png: load as-binary decompress 64#{
eJwBqAFX/olQTkcNChoKAAAADUlIRFIAAABjAAAARAgCAAAAGvAcTAAAAARnQU1B
AACxjwv8YQUAAAFfSURBVHhe7dvREoIwDERR/v+nFRQFEei2I8zIHF5JBbZ3kxRq
1zlyBW7bx86PLAblkfOB/aiTr958n8PAk++VUpuztT8TJ88TptJ5ohSlNhRorieY
whSmnvY5v0fhPu7jPu5bYUCP/qFAc6a0mpmkwxSmZgrknsojvXVJOxpKUerXvR+m
MIWpx0reF4e076MUpV4KNL9HXlQeTGEKU6tVKF9L5ZHc19j3yVPylDwlT628Gc2z
7xGRMrqMfvBKXu1T+9Q+tU/tK+1Nvsg35OcWp+9j9fHyyPfwfsiOUgddve0+C7Uv
/45y+UhK7TM9nh0xjGJLOw0xNSlQzCl59vnHSO6LHMV9hco7Zx9TmAoUqMq8mAoU
TfYlXL72RzpRivsqKhqmIgUwhakIlMI/4ZprlC4hkt9qpsKnmMJUoIDaV+GpQM8h
RJ6q0FSeiqjCFKYiUOp6VO6LRN3cc+XEtwJ3Q20XtjlRpMkAAAAASUVORK5CYIIA
8sGbqAEAAA==
}

stz-home: what-dir
sample-rate: 44100
sample-length: 44100
sample: make binary! 1000000
pitch: 440
oct: 1
vibrato-wave-length: 1
vibrato-phase: 0
vibrato-depth: 0
vibrato-amount: 0
knob-style: stylize/master [
    knob: box 50x50 with [
        colors: white
        data: 0
        outer-part: none
        inner-part: none
        guideline: 'outer
        guideline-point: 25
        angle: -45
        inner-radius: 14
        last-offset: 0x0
        feel: make feel [
            redraw: func [f a p] [
                f/data: max 0 min 1 f/data
                f/effect/draw/transform: min 225 max -45 f/data * 270 - 45
                f/effect/draw: skip find f/effect/draw 'arc 4
                f/effect/draw/1: to integer! f/data * 270
                f/effect/draw: head f/effect/draw
            ]
            engage: func [f a e] [
                if not any [
                    all [f/data = 0 f/last-offset/y <= e/offset/y]
                    all [f/data = 1 f/last-offset/y >= e/offset/y]
                ] [
                    switch a [down [f/last-offset: e/offset]]
                    f/effect/draw/transform: min 225 max -45 f/effect/draw/transform + f/last-offset/y - e/offset/y
                    f/data: (f/effect/draw/transform + 45) / 270
                    f/effect/draw: skip find f/effect/draw 'arc 4
                    f/effect/draw/1: to integer! f/data * 270
                    f/effect/draw: head f/effect/draw
                    f/last-offset: e/offset
                    do-face f f/text
                    show f
                ]
            ]
        ]
        multi: make multi [
            color: func [face blk] [
                face/colors: reduce switch length? blk [0 [[66.155.148 44.103.98 51.51.51 39.39.39 255.255.255]] 1 [[blk/1 blk/1 / 1.5 blk/1 blk/1 * 1.3 blk/1 blk/1 / 2.25]]
                    2 [[blk/1 blk/1 / 1.5 blk/2 blk/2 / 1.3 blk/1 / 2.25]]
                    3 [[blk/1 blk/1 / 1.5 blk/2 blk/2 / 1.3 blk/3]]
                    4 [[blk/1 blk/4 blk/2 blk/2 / 1.3 blk/3]]
                    5 [[blk/1 blk/4 blk/2 blk/5 blk/3]]
                ]
            ]
        ]
        words: [
            guideline [new/guideline: second args next args]
            inner-radius [new/inner-radius: second args next args]
        ]
        append init [
            inner-radius: min inner-radius (size/x / 2) - 3
            guideline-point: switch guideline [
                inner [(size/x / 2) * 0.75]
                outer [3]
            ]
            effect: compose/deep [
                draw [
                    line-width 2
                    line-cap round
                    pen (colors/1)
                    arc (size / 2) ((size / 2) - 1) 135 (data * 270) closed
                    circle (size / 2) inner-radius inner-radius
                    transform -45 (size / 2) 1 1 0x0
                    line (as-pair guideline-point size/y / 2) (as-pair (size/x / 2) - inner-radius - 1 size/y / 2)
                    line-width 1.5
                    line (as-pair guideline-point size/y / 2) (as-pair (size/x / 2) - inner-radius - 1 size/y / 2)
                ]
            ]
            effect/draw/transform: (data * 270) - 45
            angle: (data * 270) - 45
        ]
    ]
]
demo: layout [
    styles knob-style across
    knob 100x100 180.180.190 180.180.190 black inner-radius 30
    knob 15x15 250.200.170 200.200.200 guideline 'inner inner-radius 7
    knob 40x40 200.205.220 100.105.120 200.205.220 inner-radius 10
    knob inner-radius 5
    knob yellow gold orange inner-radius 17 guideline 'inner
    knob yellow gold orange inner-radius 17 guideline 'outer
    knob 100x100 180.180.190 white black inner-radius 30
]
stz-styles: stylize [
    slider: slider 150x20 220.220.230 180.180.190
    field: field 50x20 51.51.51 66.155.148 font-size 10 edge [size: 1x1 color: black] font-color 132.255.250 with [font: make font [offset: 0x0] para: make para [origin: 0x0 margin: 0x0]]
    button: button 60x21 black black 132.255.250 effect [fit contrast 10] edge [size: 1x1 color: 132.255.250 effect: none] font-size 9 font-color 132.255.250
    button-lfo: button 21x21 effect [draw [pen 180.0.0 text 3x3 "lfo" pen 90.0.0 line-width 2 line 0x0 21x21 line 0x20 20x0]]
    text: text 89x20
    inp-txt: txt 100 white gray bold
    txt: txt 132.255.250 bold
    tx: txt font-size 9 43x10 with [para: make para [origin: 0x0 margin: 0x0]]
    radio: radio 15x9 with [images: reduce leds] edge [size: 1x1 color: 51.51.51]
    radio-line: radio-line font-color 132.255.250 font-size 9 with [images: reduce leds]
    check-line: check-line font-color 132.255.250 font-size 9 with [images: reduce leds]
    bar: bar 110x1 66.155.148 edge [size: 0x0]
]
stylize/master [
    toolpanel: panel with [
        txt: ""
        type: 'toolpanel
        siz: 16
        group: 0
        closed?: no
        color: none
        edge: none
        multi: make multi [
            text: func [face blk] [
                if pick blk 1 [
                    face/txt: first blk
                    face/texts: copy blk
                ]
            ]
        ]
        words: [
            closed [
                new/closed?: yes
                next args
            ]
            group [new/group: second args next args ]
        ]
    ]
    tool: box with [
        edge: make edge [size: 1x1 color: none effect: none]
        type: 'tool
        colors: reduce [none 132.255.250]
        color: none
        effect: [merge key 255.255.255]
        feel: make feel [
            engage: func [face action event] [
                switch action [
                    time [if not face/state [face/blinker: not face/blinker]]
                    down [
                        face/state: on
                        if face/type = 'tool [
                            foreach fc face/parent-face/pane [
                                if equal? fc/type 'tool [fc/edge/color: fc/colors/1 fc/edge/effect: none]
                            ]
                        ]
                        face/edge/color: face/colors/2
                        face/edge/effect: 'ibevel
                        show face/parent-face
                    ]
                    alt-down [face/state: on]
                    up [if face/state [do-face face face/text] face/state: off]
                    alt-up [if face/state [do-face-alt face face/text] face/state: off]
                    over [face/state: on]
                    away [face/state: off]
                ]
                cue face action
                show face
            ]
        ]
        words: [
            nt [
                new/type: none
                new/colors: [200.200.210 200.200.210]
                next args
            ]
        ]
        append init [
            edge: make edge []
            size: image/size + 2x2
        ]
    ]
]
to-AIFF: func [{converts SINTEZAR sound to AIFF using SINTEZAR values}
    sample
    bits
    samples
    sample-rate
    /local COMM-chunk SSND-chunk FORM-chunk
] [
    SSND-chunk: join load rejoin [
        "#{"
        "53534e44"
        to-string to-hex bits / 8 * samples + 8
        "0000000000000000"
        "}"
    ] sample
    COMM-chunk: load rejoin [
        "#{"
        "434f4d4d000000120001"
        to-string to-hex samples
        to-string skip to-hex bits 4
        "400e"
        to-string skip to-hex sample-rate 4
        "000000000000"
        "}"
    ]
    FORM-chunk: load rejoin [
        "#{"
        "464F524D"
        to-string to-hex (
            4 + (length? COMM-chunk) + (length? SSND-chunk)
        )
        "41494646"
        "}"
    ]
    return join FORM-chunk (join COMM-chunk SSND-chunk)
]
Chunk-ID: "RIFF"
RIFF-Type-ID: "Wave"
itble4: func [n /local rslt] ["Integer To Binary Little Endian (4 bytes align)"
    rslt: copy 64#{}
    n: to string! to-hex n
    forskip n 2 [insert rslt load rejoin ["#{" n/1 n/2 "}"]]
    rslt
]
itble2: func [n /local rslt] ["Integer To Binary Little Endian (2 bytes align)"
    rslt: copy 64#{}
    n: to string! to-hex n
    forskip n 2 [insert rslt load rejoin ["#{" n/1 n/2 "}"]]
    copy/part rslt 2
]
wav-structure: func [smpl] [
    to binary! rejoin [
        to binary! "RIFF"
        itble4 ((length? format-chunk) + (length? data-chunk smpl) + 4)
        to binary! "WAVE"
        format-chunk
        data-chunk smpl
    ]
]
format-chunk: to binary! rejoin [
    to binary! "fmt "
    64#{EAA=}
    64#{AAA=}
    64#{AQA=}
    64#{AQBErA==}
    64#{AAAQsQ==}
    64#{AgA=}
    64#{BAA=}
    64#{EAA=}
]
data-chunk: func [smpl] [
    to binary! rejoin [
        to binary! "data"
        itble4 length? smpl
        smpl
    ]
]
buffer: context [
    length: 256
    data: make block! length
    init: does [
        data: make block! length
    ]
    fill: func [fnc] [
        init
        loop length [
            insert tail data do fnc
            data
        ]
    ]
]
pipe: context [
    length: 256
    data: make block! length
    init: does [
        data: make block! length
    ]
    insert: func [value] [
        insert tail data value
        if (length? data) > length [
            remove first data
        ]
    ]
]
convert: func [
    {Converts sample value to 8 or 16 bit signed or unsigned binary value}
    value "Sample value (-1..1)"
    /bit16 /signed
] [
    if bit16 [
        if signed [
            return load rejoin ["#{" to-string skip to-hex to-integer value * 32767.5 4 "}"]
        ]
        return load rejoin ["#{" to-string skip to-hex to-integer 1 + value * 32767.5 4 "}"]
    ]
    if signed [
        return load rejoin ["#{" to-string to-hex to-integer value * 127.5 "}"]
    ]
    to-binary (to-string (to-char (to-integer ((1 + value) * 127.5))))
]
if (false) [
    foreach file [
        %styles/knob.r
        %styles/stz-styles.r
        %styles/toolpanel.r
        %tools/aiff.r
        %tools/wav.r
        %tools/buffer.r
        %tools/convert.r
    ] [include file]
]
sndport: open sound://
~fnt-h1: make face/font [name: "arial black" size: 60]
~fnt-h2: make face/font [name: "arial black" size: 12]
~fnt-h3: make face/font [name: "arial black" size: 15]
~btn-chg: none
~fld-smplen: context [text: "1"]
modules: either exists? %modules/ [read %modules/] [copy []]
m-offset: 10x10
remove-each module modules [not equal? "r" last parse module "."]
forall modules [modules/1: to word! first parse modules/1 "."]
outputs: copy []
main-output: none
module-stack: copy []
modnr: 0
add-module: func [value] [
    modnr: modnr + 1
    either issue? prepared-modules [
        do rejoin [%modules/ value ".r"]
    ] [do select prepared-modules value ]
    tmp: get to word! value
    tmp-name: to word! rejoin [get in tmp 'name "-" modnr]
    insert tail module-stack set tmp-name make tmp [id: tmp-name]
    do get in get tmp-name 'init-gui
    append outputs compose/deep [(tmp-name) [(get in get tmp-name 'output)]]
]
prepared-modules: copy [] unset
prepared-modules: ["ESE" [
        ese: context [
            name: "ESE"
            info: "Envelope generator"
            type: 'generator
            id: none
            input: none
            output: 'envelope
            value: 0
            rates: [0 1 0 0 0 0 0 0]
            levels: [0 1 1 0 0 0 0 0 0]
            step: 1
            steps: 0
            length: sample-rate
            position: 1
            zoom: 1
            export: does [
                reduce ['rates head rates 'levels head levels]
            ]
            main: func [/local rate level next-level tmp] [
                either step = 9 [0] [
                    rate: rates/:step
                    level: levels/:step
                    next-level: levels/(step + 1)
                    step-length: (rate + 1 ** 14 / 1000 * sample-rate) + 1
                    step-pos: 0
                    repeat i step [step-pos: step-pos + (1 - rates/:i * sample-rate)]
                    tmp: ((steps / step-length) * (next-level - level)) + level
                    steps: steps + 1
                    if steps >= step-length [steps: 0 step: min 9 step + 1]
                    position: position + 1
                    value: tmp
                ]
            ]
            ~knb-rt1: ~knb-rt2: ~knb-rt3: ~knb-rt4: ~knb-rt5: ~knb-rt6: ~knb-rt7: ~knb-rt8:
            ~knb-lv1: ~knb-lv2: ~knb-lv3: ~knb-lv1: ~knb-lv5: ~knb-lv6: ~knb-lv7: ~knb-lv8:
            ~env-name:
            ~curve: none
            gui: none
            init-gui: does [
                gui: layout/tight compose [
                    styles stz-styles
                    style knob knob 16x16 inner-radius 3
                    style tx txt white bold font-size 9 25x12 with [origin: 0x0 margin: 0x0]
                    backdrop 66.155.148 effect [gradient 0x-1 66.155.148 51.51.51]
                    across
                    origin 0x10
                    space 2x2
                    box 150x18 effect [merge gradmul 1x0 black 127.127.127 draw [line-width 0.5 pen white fill-pen 132.255.250 font ~fnt-h3 text 0x0 "ENVELOPE" vectorial]]
                    return
                    ~curve: box 150x50 black effect [draw []] edge [size: 1x1 color: black]
                    slider 12x50 edge [size: 0x0] effect [merge luma -30] [zoom: (value * 9) + 1 show-curve]
                    across
                    return
                    box 10x10
                    return
                    space 2x2
                    tx 27 "rate"
                    ~knb-rt1: knob 132.255.250 with [data: 0] [rates/1: value show-curve]
                    ~knb-rt2: knob 132.255.250 with [data: 1] [rates/2: value show-curve]
                    ~knb-rt3: knob 132.255.250 with [data: 0] [rates/3: value show-curve]
                    ~knb-rt4: knob 132.255.250 with [data: 0] [rates/4: value show-curve]
                    ~knb-rt5: knob 132.255.250 with [data: 0] [rates/5: value show-curve]
                    ~knb-rt6: knob 132.255.250 with [data: 0] [rates/6: value show-curve]
                    ~knb-rt7: knob 132.255.250 with [data: 0] [rates/7: value show-curve]
                    ~knb-rt8: knob 132.255.250 with [data: 0] [rates/8: value show-curve]
                    box 10x5
                    return
                    tx 27 "level"
                    ~knb-lv1: knob 51.51.51 with [data: 1] [levels/2: value show-curve]
                    ~knb-lv2: knob 51.51.51 with [data: 1] [levels/3: value show-curve]
                    ~knb-lv3: knob 51.51.51 with [data: 0] [levels/4: value show-curve]
                    ~knb-lv4: knob 51.51.51 with [data: 0] [levels/5: value show-curve]
                    ~knb-lv5: knob 51.51.51 with [data: 0] [levels/6: value show-curve]
                    ~knb-lv6: knob 51.51.51 with [data: 0] [levels/7: value show-curve]
                    ~knb-lv7: knob 51.51.51 with [data: 0] [levels/8: value show-curve]
                    ~knb-lv8: knob 51.51.51 with [data: 0] [levels/9: value show-curve]
                    return
                    space 2x2
                    button 58 "init" [
                        either any [
                            equal? self module-stack/3/envelopes/1
                            equal? self module-stack/4/envelopes/1
                        ] [
                            rates: [0 1 0 0 0 0 0 0]
                            levels: [0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5]
                        ] [
                            rates: copy [0 1 0 0 0 0 0 0]
                            levels: copy [0 1 1 0 0 0 0 0 0]
                        ]
                        init-module
                    ]
                    button 58 "copy" [clipboard-envelope/rates: copy rates clipboard-envelope/levels: copy levels]
                    button 58 "paste" [rates: copy clipboard-envelope/rates levels: copy clipboard-envelope/levels init-module]
                ]
                show-curve
            ]
            show-curve: has [old-wave-length old-position] [
                old-step: step
                old-steps: steps
                old-position: position
                old-sample-rate: sample-rate
                step: 1
                steps: 0
                sample-rate: 60 / (length / sample-rate)
                bluk: make face [size: 60x21 edge: none color: black effect: [draw []]]
                bluk/effect/draw: compose [pen green line-width 2 line]
                repeat i 60 [
                    position: i * (length / 60)
                    insert tail bluk/effect/draw as-pair i (-20 * main) + 20
                ]
                if not none? ~btn-chg [
                    ~btn-chg/image: to image! bluk
                    show ~btn-chg
                ]
                step: 1
                steps: 0
                sample-rate: 14.8 * zoom
                ~curve/effect/draw: compose [
                    pen 0.0.0 line-width 0.75
                    fill-pen 0.92.28
                    box 0x0 (as-pair (to decimal! ~fld-smplen/text) * sample-rate 50)
                    pen green line-width 3 line
                ]
                repeat i 148 [
                    position: i * (length / 148)
                    insert tail ~curve/effect/draw as-pair i (-48 * main) + 48
                ]
                show ~curve
                position: old-position
                step: old-step
                steps: old-steps
                sample-rate: old-sample-rate
            ]
            init-gui
            init-module: does [
                position: 1
                steps: 0
                step: 1
                length: sample-length
                repeat i 8 [
                    set in get bind to word! join "~knb-rt" i 'gui 'data pick rates i
                    set in get bind to word! join "~knb-lv" i 'gui 'data pick levels i + 1
                ]
                show-curve
                show self/gui
            ]
            init-module
        ]
    ] "PHiDO2" [
        switch-range: func [number blk] [
            foreach [rfrom rto action] reduce blk [
                if all [number >= rfrom number < rto] [return do action]
            ]
        ]
        phido2: context [
            name: "PHiDO2"
            info: "Phase distortion oscillator"
            type: 'generator
            id: none
            input: [DCW none feedback none amplitude none]
            output: 'main
            value: none
            phase: 0
            DCA: 0.707106781186548
            DCW: 1
            DPS: 0
            ph: phw: 0
            d1: d2: h: l: b: n: f1: q: 0
            q1: q2: 1
            f1: f2: 1
            pole1: 0
            wave-length: 400
            windows: [
                none [1]
                sawtooth [1 - phw]
                triangle [2 * either phw < 0.5 [phw] [1 - phw]]
                trapezoid [either phw < 0.5 [1] [2 * (1 - phw)]]
                saw-pulse [either phw < 0.5 [1 - (phw * 2)] [1]]
            ]
            wins: copy []
            forskip windows 2 [append wins to string! windows/1]
            window: 'none
            algorithms: [
                sawtooth [
                    d: (1 - dcw) / 2
                    cosine 360 * case [0 < ph and (ph <= d) (ph / (2.0 * d))
                        d < ph and (ph <= 1) (((ph - d / (1 - d)) * 0.5 + 0.5))
                    ]
                ]
                new-sawtooth [
                    d: (1 - dcw) / 4 + 5E-2
                    sine 360 * do select reduce [0 < ph and (ph <= d) [ph / d / 4]
                        d < ph and (ph <= (1 - (d / 4))) [ph + 0.3 - d] (1 - (d / 4)) < ph and (ph <= 1) [0]
                    ] true
                ]
                square [
                    d: 1 / max 1E-24 (1 - dcw)
                    cosine 360 * case [0 < ph and (ph < 0.25) (ph * 4 ** d / 4)
                        0.25 <= ph and (ph <= 0.5) ((abs ph - 0.5) * 4 ** d / 4 + 0.5)
                        0.5 < ph and (ph <= 0.75) (ph * 4 - 2 ** d / 4 + 0.5)
                        0.75 < ph and (ph <= 1) ((abs ph - 1) * 4 ** d / 4 + 1)
                    ]
                ]
                pulse [
                    d: 0.875 * (1 - dcw) + 0.125
                    cosine 360 * case [0 < ph and (ph <= d) (ph / d)
                        d < ph and (ph <= 1) 0
                    ]
                ]
                saw-pulse [
                    d: 1 - dcw / 2 + 0.5
                    cosine 360 * case [0 < ph and (ph <= d) ph
                        d < ph and (ph <= 1) 1
                    ]
                ]
                sine-sync [cosine 360 * ph * (1 + (15 * dcw)) ]
                triangle [
                    d: 1 - (dcw * 0.41111)
                    cosine 360 * case [0 < ph and (ph <= 0.25) (ph * 4 ** d / 4)
                        0.25 < ph and (ph <= 0.5) ((abs ph - 0.5) * 4 ** d / 4 + 0.5)
                        0.5 < ph and (ph <= 0.75) (ph * 4 - 2 ** d / 4 + 0.5)
                        0.75 < ph and (ph <= 1) ((abs ph - 1) * 4 ** d / 4 + 1)
                    ]
                ]
                no-wave [none]
            ]
            algorithm: [sawtooth no-wave]
            octave-modulation: true
            curves: copy []
            forskip algorithms 2 [append curves to string! algorithms/1]
            filters: [
                none [snd-value]
                low-pass [q1 * l]
                by-pass [q1 * b]
                hi-pass [q1 * h]
                notch [n]
            ]
            filts: copy [] forskip filters 2 [append filts to string! filters/1]
            filter: 'none
            export: has [enves] [
                enves: copy []
                foreach env envelopes [append/only enves env/export]
                reduce ['DCA DCA 'DCW DCW 'DPS DPS 'q q 'f1 f1 'wave-length wave-length 'window window 'algorithm head algorithm 'filter filter 'envelopes enves]
            ]
            sync?: false
            phaser: func [] [
                sync?: false
                phase: 1 / (wave-length * (2 ** vibrato-amount) * (4 ** (envelopes/1/value - 0.5 * -2))) + phase
                if phase >= 1 [
                    sync?: true
                    phase: phase - 1
                    if not none? algorithm/2 [
                        octave-modulation: not octave-modulation
                    ]
                ]
                phase
            ]
            envelopes: make block! 6
            env-backup: make block! 6
            loop 6 [append envelopes make ese []]
            envelopes/1/rates: [0 1 0 0 0 0 0 0]
            envelopes/1/levels: [0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5]
            foreach envelope envelopes [envelope/init-module envelope/init-gui]
            init-envs: does [
                foreach envelope envelopes [envelope/init-module]
            ]
            dummy-env: context [main: [] value: 1]
            dummy-envs: does [
                env-backup: copy envelopes
                envelopes: make block! 6
                loop 6 [append envelopes make dummy-env []]
                envelopes/1/value: 0.5
            ]
            revert-envs: does [envelopes: copy env-backup ]
            init-envs
            init-module: does [
                if not none? algorithm/2 [
                    octave-modulation: not octave-modulation
                ]
                ~rot-flt/data: find head ~rot-flt/data to string! filter
                ~knb-dcw/data: ~fld-dcw/text: dcw
                ~knb-dps/data: ~fld-dps/text: dps
                ~knb-cut/data: ~fld-cut/text: f1
                ~knb-res/data: ~fld-res/text: q
                ~knb-dca/data: (square-root dca) / 2
                ~fld-dca/text: 20 * log-10 dca
                repeat i 7 [set in get bind to word! join "~rw1" i 'gui 'data off]
                set in get bind to word! join "~rw1" (index? find algorithms algorithm/1) + 1 / 2 'gui 'data on
                repeat i 8 [set in get bind to word! join "~rw2" i 'gui 'data off]
                set in get bind to word! join "~rw2" (index? find algorithms algorithm/2) + 1 / 2 'gui 'data on
                repeat i 5 [set in get bind to word! join "~rwi" i 'gui 'data off]
                set in get bind to word! join "~rwi" (index? find windows window) + 1 / 2 'gui 'data on
                show gui
                show-curve
                init-envs
            ]
            main: func [] [
                ph: phaser
                foreach env envelopes [env/main]
                algo: pick algorithm octave-modulation
                if equal? 'no-wave algo [algo: pick algorithm not octave-modulation]
                either equal? 'no-wave algorithm/2 [phw: ph ] [
                    phw: ph / 2 + either octave-modulation [0] [0.5]
                ]
                dcws: dcw
                dcw: dcw * envelopes/2/value
                ph: min 1 (1.0 + (dps * envelopes/3/value)) ** 6 * ph
                snd-value: 2 * ((do select windows window) * (((do select algorithms algo) - 1) / 2)) + 1
                dcw: dcws
                q1: 1 / max 1E-24 q * 3.0 * envelopes/5/value + 1.0
                f2: sine (f1 * envelopes/4/value * 90.0)
                d2: l: f2 * d1 + d2
                h: snd-value - l - (q1 * d1)
                d1: b: f2 * h + d1
                n: h + l
                value: min 1 max -1 envelopes/6/value * dca * (do select filters filter)
            ]
            ~knb-dcw: ~knb-dps: ~knb-cut: ~knb-res: ~knb-dca:
            ~fld-dcw: ~fld-dps: ~fld-cut: ~fld-res: ~fld-dca:
            ~txt-amp:
            ~dd1: ~dd2:
            ~b1: ~b2: ~b3: ~b4: ~b5: ~b6:
            ~rw11: ~rw12: ~rw13: ~rw14: ~rw15: ~rw16: ~rw17:
            ~rw21: ~rw22: ~rw23: ~rw24: ~rw25: ~rw26: ~rw27: ~rw28:
            ~rwi1: ~rwi2: ~rwi3: ~rwi4: ~rwi5:
            ~rot-flt:
            ~name:
            ~curve: none
            waves.img: load as-binary decompress 64#{
eJwB/AID/YlQTkcNChoKAAAADUlIRFIAAAAjAAAAkQgCAAAAxrmWcAAAAAFzUkdC
AK7OHOkAAAAEZ0FNQQAAsY8L/GEFAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1
MAAA6mAAADqYAAAXcJy6UTwAAAJ6SURBVGhD7ZjrbsMgDIX7bHuyPcT+7WG7qFQJ
NQYfXyBL5CmaNtX1hy9wTB6PlT/P5/f2fP3+zHve4RTMVN4HqQQ0iceQJvG6pHCe
QArkQaQQnoLk5KlJZp6RZOC5SCpeAAnkhZFEXjBpwJtCYnkTSYQ3nbTzVsrti1U0
SfWo1HnzbMyedhRIUl2YzN5r3sre25siOyI74vNqddOOUInZcUask17POQ2K7zuY
E0j71RpcKW5GY0qS5k3JR/bqna89BcSCJUlM0cDgyF5bmNhSJSmgTr2SBJZKfuvm
iaP+rkzSamvPfp3YBihhHQS0c20zN2mZQQe5lJD124PZSYPlsx/9e5K4o1sDoxLe
jiQGtL//Yk6j9jNta7V7lniwKKE3JlzdF5FATFsOdfa8JFBzVRgSlqyEtjO+vZDJ
b6h2+fHI/Nn33N7ayYCgDZFRd+KCzZ6hNUakwmDXHkka+wojiY5EgzYTxiksgAS6
AM26+rR9ALoAzSAlHG+Xi5BUy1QZl/SM9GmQwCuQtGvU2h/Z035Ta38REpEr8d9T
77mImNrrhHgnQ5IWJqt7K/a2uUxHauPAI1OQnDMFSvKPFRAJSZFoI5NEF2CDCCQc
w95tR3NEe0dQbbXBJNKNSRuNuESe5MH00micLJGUkuXyb6gQR4hNDeu+N0IcITY7
zDgbIQyyz+R7rsppz3iL7CTNrQsY8jfUESGk+sigHVEAM34nyZPVG2evTNWTuq44
p6fRDF4JgFeNWB6959ZamSTp9Do7e2RgizrRjxt1b1qLJ62T3pBhYexEvmtELSJJ
nkxm9gKyV2Rx6pN1CqiTxwX43awTmCjWbHn21mjuH74rxHHG2MGHAAAAAElFTkSu
QmCCLDJbrvwCAAA=
}

            windows.img: load as-binary decompress 64#{
eJzrDPBz5+WS4mJgYOD19HAJAtLKQBzNwQQky06tXgSkWNIdfR0ZGDb2c/9JZGVg
YOzzdHEMyXB+eyO677ACD+uFdblhZt0v7utsE+7s+LTlvalZkPYqj78rPgbyHlTp
P37u/Ouv0rV3658n1f2bV3h96n+PZ5/mvZrpXc5rc9X66urv2zYr2eW6vnm9a+XD
dVfcZaNLDwXkXyhu3pNZXv59SnHLYva5ETE2Hl6vsy4qL3Htn3ZmXqBRhbfQU0uP
Bc/VVs7brFKheOkC9xQp/ZhT9/alVXtnHc9h4uG9paTadjRjJYtGz+RfVyzcPDYv
UWjzKrJiaVZRmSi12Durq4b5xMSuFuNK/Rl3Z006YmRkNjWnp6F3qWhaG++S0rSO
rZPELydPfXLa5EZ0V4LXm+7qvKq+BXrbfG+vPn5udWyS9J9JLomrhfosPRSeKSvO
mL0kad6kHktvpmf6ZzcIZHa4etqKaZhGrAlJehh0rMyDc/fS2vic6MW7irnqwvsP
rXiwqipTxU6uP+e++5OJCVwGa2q2SK2xVeEWCpabt6oyq24Cf2TeJKt0vEbeVllp
7bF/8tt3O//P9H+v/sBK7i4wphg8Xf1c1jklNAEArsXPI9cBAAA=
}

            gui: none
            init-gui: does [
                gui: layout/tight compose copy/deep [
                    styles knob-style
                    styles stz-styles
                    style text text 69x20 132.255.250
                    style field field 69x20
                    style knob knob 21x21 inner-radius 3
                    style radio radio 15x9 effect [merge alphamul 50]
                    origin 2x4
                    space 2x2
                    backdrop 33.77.74
                    panel [
                        ~name: box 15x180
                        return
                        panel [
                            space 2x5
                            txt "WAVE"
                            box 37x148 effect [draw [image waves.img 0x0 35x146]] edge [size: 2x2 color: black]
                            return
                            txt "1"
                            box 5x5
                            panel [
                                space 5x9
                                ~rw11: radio [algorithm/1: 'sawtooth show-curve] on
                                ~rw12: radio [algorithm/1: 'square show-curve]
                                ~rw13: radio [algorithm/1: 'pulse show-curve]
                                ~rw14: radio [algorithm/1: 'square show-curve]
                                ~rw15: radio [algorithm/1: 'saw-pulse show-curve]
                                ~rw16: radio [algorithm/1: 'triangle show-curve]
                                ~rw17: radio [algorithm/1: 'sine-sync show-curve]
                            ]
                            return
                            txt "2"
                            box 5x5
                            panel [
                                space 5x9
                                ~rw21: radio [algorithm/2: 'sawtooth show-curve]
                                ~rw22: radio [algorithm/2: 'square show-curve]
                                ~rw23: radio [algorithm/2: 'pulse show-curve]
                                ~rw24: radio [algorithm/2: 'square show-curve]
                                ~rw25: radio [algorithm/2: 'saw-pulse show-curve]
                                ~rw26: radio [algorithm/2: 'triangle show-curve]
                                ~rw27: radio [algorithm/2: 'sine-sync show-curve]
                                ~rw28: radio [algorithm/2: 'no-wave show-curve] on
                            ]
                            across
                            tx 80 "DCO......ENV/LFO"
                            return
                            ~b1: button [~p4/pane: envelopes/1/gui ~btn-chg: ~b1 envelopes/1/init-module show ~p4]
                            button-lfo
                        ]
                    ]
                    return
                    bar 3x220
                    return
                    panel [
                        space 2x3
                        panel [
                            tx "window"
                            box 37x94 effect [draw [image windows.img 0x0 35x92]] edge [size: 2x2 color: black]
                            return
                            tx ""
                            box 5x0
                            panel [
                                space 5x9
                                ~rwi1: radio [window: 'none show-curve] on
                                ~rwi2: radio [window: 'sawtooth show-curve]
                                ~rwi3: radio [window: 'triangle show-curve]
                                ~rwi4: radio [window: 'trapezoid show-curve]
                                ~rwi5: radio [window: 'saw-pulse show-curve]
                            ]
                        ]
                        bar 90x2
                        across
                        tx 30 "DCW"
                        ~fld-dcw: tx 30 "1.0"
                        ~knb-dcw: knob with [data: 1] [dcw: value ~fld-dcw/text: DCW show ~fld-dcw show-curve]
                        return
                        ~b2: button [~p4/pane: envelopes/2/gui ~btn-chg: ~b2 envelopes/2/init-module show ~p4]
                        button-lfo
                        return
                        tx 30 "DPS"
                        ~fld-dps: tx 30 "0.0"
                        ~knb-dps: knob with [data: 0] [dps: value ~fld-dps/text: DPS show ~fld-dps show-curve]
                        return
                        ~b3: button [~p4/pane: envelopes/3/gui ~btn-chg: ~b3 envelopes/3/init-module show ~p4]
                        button-lfo
                    ]
                    return
                    bar 3x220
                    return
                    ~curve: box 150x50 effect [merge luma -70 grid 15x6 0x2 66.155.148 draw []] edge [size: 1x1 color: black]
                    panel [
                        across
                        box 16x150 effect [merge gradmul 0x1 black 127.127.127 draw [line-width 0.5 pen white fill-pen 132.255.250 font ~fnt-h3 rotate -90 translate -47x-2 text 0x0 "DCF" vectorial]]
                        panel [
                            across
                            origin 10x2
                            space 2x2
                            tx 50x20 "Filter type" ~rot-flt: rotary 66.155.148 60x20 "none" "low-pass" "by-pass" "notch" "hi-pass" [filter: to word! value show-curve] font-size 10 edge [color: 66.155.148]
                            return
                            tx 30 "CUT"
                            ~fld-cut: tx 30 "1.0"
                            ~knb-cut: knob with [data: 1] [f1: value ~fld-cut/text: f1 show ~fld-cut show-curve]
                            return
                            ~b4: button [~p4/pane: envelopes/4/gui ~btn-chg: ~b4 envelopes/4/init-module show ~p4]
                            button-lfo
                            return
                            tx 30 "RES"
                            ~fld-res: tx 30 "0.0"
                            ~knb-res: knob [q: value ~fld-res/text: value show ~fld-res show-curve]
                            return
                            ~b5: button [~p4/pane: envelopes/5/gui ~btn-chg: ~b5 envelopes/5/init-module show ~p4]
                            button-lfo
                            return
                            tx 30 "DCA"
                            ~fld-dca: tx 30 "-3.0"
                            ~knb-dca: knob with [data: 0.42] [dca: value * 2 ** 2 ~fld-dca/text: either zero? dca ["-OO"] [round/to 20 * log-10 dca 1E-2] show ~fld-dca show-curve]
                            return
                            ~b6: button [~p4/pane: envelopes/6/gui ~btn-chg: ~b6 envelopes/6/init-module show ~p4]
                            button-lfo
                            return
                        ]
                    ]
                ]
                show-curve
            ]
            show-curve: has [old-wave-length old-phase old-vibrato-amount] [
                octave-modulation: true
                old-wave-length: wave-length
                old-phase: phase
                old-vibrato-amount: vibrato-amount
                dummy-envs
                ~curve/effect/draw: copy [pen green line-width 1.5 line]
                phase: 0
                wave-length: 75
                repeat i 2 * wave-length [insert tail ~curve/effect/draw as-pair i 23 + (-23 * main) ]
                show ~curve
                phase: old-phase
                wave-length: old-wave-length
                vibrato-amount: old-vibrato-amount
                revert-envs
            ]
            init-gui
        ]
    ] "RMX" [
        average: func [blk /local tmp] [
            tmp: 0
            repeat i length? blk [tmp: tmp + blk/:i]
            tmp / length? blk
        ]
        RMX: context [
            name: "RMX"
            info: "Envelope generator"
            type: 'effect
            id: none
            input: none
            output: none
            value: 0
            osc1: none
            osc2: none
            dca: 1
            shaper: 0.5
            d1: d2: h: l: b: n: f1: q: 0
            q: q1: q2: 1
            f1: f2: 1
            bit-res: none
            freq-div: 1
            div-value: 0
            div-cache: copy []
            modes: [
                first-line [osc1]
                second-line [osc2]
                mix [(osc1 + osc2) / 2.0]
                ring [osc1 * osc2]
                ring2 [osc1 + 1.0 / 2.0 * (osc2 + 1.0 / 2.0) * 2.0 - 1.0]
                mix-ring [(osc1 * osc2 + osc2) / 2.0]
                noise [osc1 + (osc2 * (random 10000000.0) / 10000000.0) / 2.0]
            ]
            mode: 'first-line
            filters: [
                none [snd-value]
                low-pass [q1 * l]
                by-pass [q1 * b]
                hi-pass [q1 * h]
                notch [n]
            ]
            filts: copy [] forskip filters 2 [append filts to string! filters/1]
            filter: 'none
            export: has [enves] [
                enves: reduce [module-stack/1/export module-stack/2/export module-stack/6/export]
                reduce ['mode mode 'filter filter 'q q 'f1 f1 'envelopes enves]
            ]
            main: func [/local tmp] [
                vibrato-phase: 1 / vibrato-wave-length + vibrato-phase // 1.0
                vibrato-amount: vibrato-depth * sine 360.0 * vibrato-phase
                module-stack/1/main
                module-stack/2/main
                module-stack/3/main
                if ~chk-sync/data and module-stack/3/sync? [module-stack/4/phase: 0]
                module-stack/4/main
                module-stack/6/main
                osc1: module-stack/3/value
                osc2: module-stack/4/value
                snd-value: do select modes mode
                q1: 1 / max 1E-24 q * 3.0 * module-stack/2/value + 1.0
                f2: sine (f1 * module-stack/1/value * 90.0)
                l: f2 * b + l
                h: snd-value - l - (q1 * b)
                b: f2 * h + b
                n: h + l
                value: dca * module-stack/6/value * min 1 max -1 do select filters filter
                if ~chk-shaper/data [
                    absval: abs value
                    value: (1 / (shaper + 1.0 / 2.0)) * (sign? value) * case [
                        absval < shaper absval
                        absval > 1 (shaper + 1.0 / 2.0)
                        absval >= shaper (shaper + ((absval - shaper) / ((absval - shaper) / (1.0 - shaper) ** 2.0 + 1.0)))
                    ]
                ]
                if not none? bit-res [value: (to integer! value * (2 ** bit-res)) / (2 ** bit-res)]
                insert tail div-cache value
                if freq-div <= length? div-cache [div-value: average div-cache div-cache: copy []]
                value: div-value
            ]
            ~t1: ~t2: ~t3:
            ~b1: ~b2: ~b3:
            ~knb-cut: ~knb-res: ~knb-dca:
            ~rot-flt:
            ~curve: none
            gui: none
            init-gui: does [
                gui: layout/tight compose [
                    styles stz-styles
                    style knob knob 21x21 inner-radius 3
                    backdrop 66.155.148 effect [gradient 0x1 66.155.148 51.51.51]
                    origin 2
                    space 2x2
                    across
                    panel [
                        space 0x1
                        tx 85 "Line selection"
                        ~rl1: radio-line "1" [mode: 'first-line] on
                        ~rl2: radio-line "2" [mode: 'second-line]
                        ~rl3: radio-line "1 + 2" [mode: 'mix]
                        ~rl4: radio-line "1 + 1'"
                    ] edge [size: 1x1 color: black]
                    panel [
                        style radio-line radio-line 70x13
                        space 0x0
                        tx 85 "Modulation"
                        ~rm1: radio-line "no" [mode: 'mix] on
                        ~rm2: radio-line "ring" [mode: 'ring]
                        ~rm5: radio-line "ring2" [mode: 'ring2]
                        ~rm3: radio-line "mix-ring" [mode: 'mix-ring]
                        ~rm4: radio-line "noise" [mode: 'noise]
                    ] edge [size: 1x1 color: black]
                    return
                    bar 180 return
                    box 150x18 effect [merge gradmul 1x0 black 127.127.127 draw [line-width 0.5 pen white fill-pen 132.255.250 font ~fnt-h3 text 0x0 "DCF + DCA" vectorial]]
                    return
                    tx 110 "Filter type"
                    ~rot-flt: rotary 66.155.148 60x20 "none" "low-pass" "by-pass" "notch" "hi-pass" [filter: to word! value] font-size 10 edge [color: 66.155.148]
                    return
                    tx "CUTOFF" ~t1: tx "1" ~knb-cut: knob 132.255.250 [f1: value ~t1/text: round/to value 1E-2 show ~t1] with [data: 1]
                    ~b1: button [~p4/pane: module-stack/1/gui ~btn-chg: ~b1 module-stack/1/init-module show ~p4] return
                    tx "RESO" ~t2: tx "0" ~knb-res: knob 132.255.250 [q: value ~t2/text: round/to value 1E-2 show ~t2] with [data: 0]
                    ~b2: button [~p4/pane: module-stack/2/gui ~btn-chg: ~b2 module-stack/2/init-module show ~p4] return
                    tx "DCA" ~t3: tx "0.0" ~knb-dca: knob 132.255.250 [dca: value * 2 ** 2 value ~t3/text: either zero? dca ["-OO"] [round/to 20 * log-10 dca 1E-2] show ~t3] with [data: 0.5]
                    ~b3: button [~p4/pane: module-stack/6/gui show ~p4 ~btn-chg: ~b3]
                    return
                    box 150x18 effect [merge gradmul 1x0 black 127.127.127 draw [line-width 0.5 pen white fill-pen 132.255.250 font ~fnt-h3 text 0x0 "FinalCrusher" vectorial]]
                    return
                    ~chk-shaper: check-line "Waveshaper" 90 ~knb-wsh: knob 132.255.250 with [data: 0.5] [shaper: to decimal! value] return
                    ~tx-bit: tx "Bit resolution: full" 90 knob with [data: 1] [either value = 1 [~tx-bit/text: "Bit resolution: full" bit-res: none] [bit-res: to integer! value * 25 ~tx-bit/text: join "Bit resolution: " bit-res] show ~tx-bit] 132.255.250 return
                    ~tx-div: tx "Freq. divider: 1" 90 knob with [data: 1] [freq-div: to integer! 1 - value + 1 ** 6 ~tx-div/text: join "Freq. divider: " freq-div show ~tx-div] 132.255.250 return
                ]
            ]
            init-gui
            init-module: does [
                repeat i 4 [set in get bind to word! join "~rl" i 'gui 'data off]
                repeat i 4 [set in get bind to word! join "~rm" i 'gui 'data off]
                switch mode [
                    first-line [~rl1/data: on]
                    second-line [~rl2/data: on]
                    mix [~rl3/data: on ~rm1/data: on]
                    ring [~rl3/data: on ~rm2/data: on]
                    mix-ring [~rl3/data: on ~rm3/data: on]
                    ring2 [~rl3/data: on ~rm5/data: on]
                    noise [~rl3/data: on ~rm4/data: on]
                ]
                ~rot-flt/data: find head ~rot-flt/data to string! filter
                ~knb-cut/data: ~t1/text: f1
                ~knb-res/data: ~t2/text: q
                show self/gui
            ]
        ]
    ]]
add-module "ese"
add-module "ese"
add-module "phido2"
add-module "phido2"
add-module "rmx"
add-module "ese"
module-stack/1/init-module
module-stack/1/init-gui
module-stack/2/init-module
module-stack/2/init-gui
module-stack/6/init-module
module-stack/6/init-gui
detune-osc: has [freq] [
    freq: sample-rate / module-stack/3/wave-length
    freq: freq * (2 ** to integer! ~det-oct/text) * (1.0594630943593 ** to integer! ~det-not/text) * (1.00057778950655 ** to integer! ~det-cen/text)
    module-stack/4/wave-length: sample-rate / freq
]
export-wav: does [
    wav-sample: copy 64#{}
    forall sauple [
        insert tail wav-sample itble2 to integer! 32767 * sauple/1 - 1
    ]
    wav: wav-structure wav-sample
    attempt [write/binary first request-file/save wav]
]
export: does [
    attempt [save/header first request-file/save reduce [
            'OSC1 module-stack/3/export
            'OSC2 module-stack/4/export
            'MIX module-stack/5/export
            'DETUNE reduce [~det-oct/text ~det-not/text ~det-cen/text]
            'VIBRATO reduce [vibrato-depth vibrato-wave-length]
            'PITCH reduce [pitch]
            'LENGTH reduce [sample-length]
            'SYNC reduce [~chk-sync/data]
        ] compose [
            Title: "Sintezar PM-101 Sound"
            Version: 0.0.1
            Date: (now)
        ]]
]
import: has [tmp-preset osc1 osc1-envs osc2 osc2-envs mix] [
    tmp-preset: attempt [load/header first request-file]
    if found? find tmp-preset 'osc1 [
        osc1-envs: last tmp-preset/osc1
        osc1: head remove/part back back tail tmp-preset/osc1 2
        foreach [word value] osc1 [set in module-stack/3 word value]
        repeat i 6 [
            module-stack/3/envelopes/:i/rates: osc1-envs/:i/rates
            module-stack/3/envelopes/:i/levels: osc1-envs/:i/levels
        ]
        module-stack/3/init-module
        osc2-envs: last tmp-preset/osc2
        osc2: head remove/part back back tail tmp-preset/osc2 2
        foreach [word value] osc2 [set in module-stack/4 word value]
        repeat i 6 [
            module-stack/4/envelopes/:i/rates: osc2-envs/:i/rates
            module-stack/4/envelopes/:i/levels: osc2-envs/:i/levels
        ]
        module-stack/4/init-module
        module-stack/1/rates: tmp-preset/mix/envelopes/1/rates
        module-stack/1/levels: tmp-preset/mix/envelopes/1/levels
        module-stack/2/rates: tmp-preset/mix/envelopes/2/rates
        module-stack/2/levels: tmp-preset/mix/envelopes/2/levels
        module-stack/6/rates: tmp-preset/mix/envelopes/3/rates
        module-stack/6/levels: tmp-preset/mix/envelopes/3/levels
        mix: head remove/part back back tail tmp-preset/mix 2
        foreach [word value] mix [set in module-stack/5 word value]
        module-stack/5/init-module
        ~det-oct/text: tmp-preset/detune/1
        ~det-not/text: tmp-preset/detune/2
        ~det-cen/text: tmp-preset/detune/3
        show reduce [~det-oct ~det-not ~det-cen]
        vibrato-depth: to decimal! tmp-preset/vibrato/1
        ~vib-dep/text: to string! tmp-preset/vibrato/1
        vibrato-wave-length: to decimal! tmp-preset/vibrato/2
        ~vib-rat/text: sample-rate / vibrato-wave-length
        show reduce [~vib-rat ~vib-dep]
        pitch: tmp-preset/pitch/1
        sample-length: tmp-preset/length/1
        ~fld-smplen/text: to string! sample-length / sample-rate
        show ~fld-smplen
        ~chk-sync/data: first reduce tmp-preset/sync
        show ~chk-sync
    ]
]
sauple: copy 64#{}
make-sound: does [
    module-stack/3/wave-length: sample-rate / (oct * pitch)
    freq: sample-rate / module-stack/3/wave-length
    freq: freq * (2 ** to integer! ~det-oct/text) * (1.0594630943593 ** to integer! ~det-not/text) * (1.00057778950655 ** to integer! ~det-cen/text)
    module-stack/4/wave-length: sample-rate / freq
    module-stack/3/phase: 0
    module-stack/4/phase: 0
    vibrato-phase: 0
    sample: make binary! sample-length
    sauple: copy []
    x: now/time/precise
    loop sample-length [
        module-stack/5/main
        insert tail sample do rejoin ["#{" skip to-hex to integer! (module-stack/5/value + 1) * 32767.5 4 "}"]
        insert tail sauple module-stack/5/value
        if (length? sample) // 100 = 0 [~prog/data: (length? sauple) / sample-length show ~prog]
    ]
    ~ttt/text: now/time/precise - x
    show ~ttt
    ~sample-window/effect/draw: copy [pen green fill-pen linear 0x0 0 100 90 1 1 red yellow green yellow red line-width 0.5 polygon 0x50]
    length: 690
    repeat i length [insert tail ~sample-window/effect/draw as-pair i (pick sauple (length? sauple) / length * i) * -50 + 50 ]
    insert tail ~sample-window/effect/draw 690x50
    show ~sample-window
    module-stack/1/init-module
    module-stack/2/init-module
    module-stack/6/init-module
    foreach env module-stack/3/envelopes [env/init-module]
    foreach env module-stack/4/envelopes [env/init-module]
]
bold32: make face/font [style: 'bold size: 32]
bold12: make face/font [size: 12]
pitches: [261.625565300616 293.66476791743 329.627556912897 349.228231433035 391.995435981787 440 493.883301256181 ]
intro-image: to image! make face [
    edge: none
    color: none
    size: 703x584
    effect: [
        merge
        key 0.0.0
        draw [
            image-filter bilinear
            image logo.png 170x110 510x110 550x270 130x270 0.0.0
            image logo.png 180x100 500x100 540x280 140x280 0.0.0
            image logo.png 190x90 490x90 530x290 150x290 0.0.0
        ]
        blur sharpen blur contrast 50 blur sharpen blur
        luma -30
        colorize 132.255.250
    ]
]
synth: layout [
    backdrop 51.51.51
    styles stz-styles
    styles knob-style
    style text txt bold 132.255.250 font-size 11 with [origin: 0x0 margin: 0x0]
    style knob knob 16x16 inner-radius 3 66.155.148 51.51.51 255.255.255
    style button button 40x26 effect [merge multiply 30.50.50] edge [size: 2x2 color: 30.50.50] font-size 8
    style tx txt white bold font-size 9 43x12 with [origin: 0x0 margin: 0x0]
    style panel panel edge [color: 66.155.148 size: 0x0 effect: none]
    style imag image 24x24 font-size 9 font-color 132.255.250 with [
        font: make font [valign: 'bottom]
        feel: make feel [
            over: func [f a p] [
                either a [
                    append f/effect [luma 100]
                ] [remove back back tail f/effect 2 ]
                show f
            ]
            engage: func [face action event] [
                remove/part find face/effect 'luma 2
                switch action [
                    time [if not face/state [face/blinker: not face/blinker]]
                    down [face/state: on]
                    alt-down [face/state: on]
                    up [if face/state [do-face face face/text] face/state: off]
                    alt-up [if face/state [do-face-alt face face/text] face/state: off]
                    over [face/state: on]
                    away [face/state: off]
                ]
                cue face action
                show face
            ]
        ]
    ]
    origin 5
    space 2x2
    below
    ~p0: panel 120x442 [
        size 118x440
        styles stz-styles
        style knob knob 16x16
        style button button 40x21 effect [merge multiply 30.50.50] edge [size: 2x2 color: 30.50.50] font-size 8
        backdrop 66.155.148 effect [gradient 0x-1 66.155.148 51.51.51 grid 0x0 66.155.148]
        origin 0
        space 0
        across
        image logo.png effect [key black colorize 132.255.250 blur]
        return
        text font-size 10 132.255.250 "phase manipulation digital synthesizer" 110x30
        return
        panel [
            space 2x2
            below
            box 100x18 effect [merge gradmul 1x0 black 127.127.127 draw [line-width 0.5 pen white fill-pen 132.255.250 font ~fnt-h3 text 0x0 "detune" vectorial]]
            panel [
                tx 38x15 "OCT"
                tx 38x15 "NOTE"
                tx 38x15 "CENT"
                return
                ~det-oct: tx 25x15 "0"
                ~det-not: tx 25x15 "0"
                ~det-cen: tx 25x15 "0"
                return
                knob with [data: 0.5] [~det-oct/text: to integer! value - 0.5 * 4 show ~det-oct detune-osc]
                knob with [data: 0.5] [~det-not/text: to integer! value - 0.5 * 24 show ~det-not detune-osc]
                knob with [data: 0.5] [~det-cen/text: to integer! value - 0.5 * 200 show ~det-cen detune-osc]
            ]
            return
        ] edge [size: 0x0]
        return
        panel [
            below
            space 2x2
            box 100x18 effect [merge gradmul 1x0 black 127.127.127 draw [line-width 0.5 pen white fill-pen 132.255.250 font ~fnt-h3 text 0x0 "vibrato" vectorial]]
            panel [
                styles stz-styles
                space 28x2
                tx 38x15 "RATE"
                ~vib-rat: field 50x15 "0" [vibrato-wave-length: sample-rate / (max 1E-24 to decimal! value) ~knb-vib-rat/data: (to decimal! value) / 1000 ** 0.1 show ~knb-vib-rat]
                tx 38x15 "DEPTH"
                ~vib-dep: tx 50x15 "0"
                space 30x17
                return
                ~knb-vib-rat: knob [~vib-rat/text: round/to value ** 10 * 1000 1E-4 vibrato-wave-length: sample-rate / (max 1E-24 to decimal! ~vib-rat/text) show ~vib-rat detune-osc]
                ~knb-vib-dep: knob [~vib-dep/text: round/to vibrato-depth: value ** 3 1E-6 show ~vib-dep detune-osc]
            ] edge [size: 0x0]
            toolpanel group 1 [
                origin 0
                space 0
                across
                tool sine.png
                tool triangle.png
                tool pulse.png
                tool saw.png
            ]
        ] edge [size: 0x0]
        return
        ~chk-sync: check-line "OSC SYNC"
        return
        radio [oct: 0.125] radio [oct: 0.25] radio [oct: 0.5] radio [oct: 1] on radio [oct: 2] radio [oct: 4] radio [oct: 8]
        return
        box 2x2 image keyboard.png effect [draw []] with [
            feel: make feel [
                engage: func [f a e] [
                    switch a [
                        down [
                            either e/offset/y > 41 [
                                pitch: (pick pitches 1 + to integer! e/offset/x / 14)
                                f/effect/draw: compose [pen red circle (as-pair 14 * (to integer! e/offset/x / 14) + 7 60) 5 5]
                                show f
                            ] [print "pultony" ]
                        ]
                    ]
                ]
            ]
        ]
    ] edge [size: 1x1 color: 66.155.148]
    return
    ~p1: panel 376x220 []
    ~p2: panel 376x220 []
    return
    space 0
    ~p3: panel 182x280 []
    ~p4: panel 181x190 []
    return
    space 0
    at 5x451 ~sample-window: box 687x104 51.51.51 effect [grid 43x25 0x12 33.78.74 grid 86x25 0x0 66.155.148 draw []] edge [size: 2x2 color: 66.155.148]
    panel [
        styles stz-styles
        space 2
        across
        backdrop effect [gradient 0x-1 51.51.51 66.155.148]
        imag toolbox-images/new-project effect [key white invert colorize 132.255.250 fit]
        imag toolbox-images/open-project effect [key white colorize 132.255.250 fit] [import]
        imag toolbox-images/save-project effect [key white colorize 132.255.250 fit] "rps" [export]
        imag toolbox-images/save-project effect [key white colorize 132.255.250 fit] "wav" [export-wav]
        imag toolbox-images/make-sound effect [key white invert colorize 132.255.250 fit] [make-sound]
        imag toolbox-images/play-sound effect [key white invert colorize 132.255.250 fit] [
            snd: make sound [rate: 44100 bits: 16 volume: 1 data: sample]
            if not empty? sample [
                insert sndport snd
                wait []
            ]
        ]
        tx "length" ~fld-smplen: field "1" [
            sample-length: to integer! sample-rate * to decimal! value
            module-stack/1/init-module
            module-stack/2/init-module
            module-stack/3/init-module
            module-stack/4/init-module
            module-stack/6/init-module
        ]
        ~ttt: tx white 100x12
        ~prog: progress 115 51.51.51 132.255.250 edge [size: 1x1]
    ]
    at 0x0 image intro-image effect [
        key 0.0.0
        merge luma -70 blur blur blur contrast 50 blur blur blur luma -30 gradmul 0x-1 80.80.80 127.127.127
    ]
    at 0x270 ~bb: box 698x100 effect [
        draw [
            line-width 0.5 pen 132.255.250 fill-pen linear 300x0 0 100 30 1 1 black white black white black white reflect font bold32 text 65x5 "Phase Manipulation Digital Synthesizer" vectorial
            font bold12 text 500x45 "written by REBolek, (c) 2005" vectorial
            pen 250.255.250 line-width 0.1
            font bold12 text 280x80 "press here to continue" vectorial
        ]
        emboss
        colorize 132.255.250
    ] [remove/part back back tail synth/pane 2 show synth] with [
        saved-area: true
        rate: 0
        feel: make feel [
            n: 0
            engage: func [face action event] [
                switch action [
                    time [
                        n: n + 2
                        change skip face/effect/draw 6 as-pair n 0
                        change skip face/effect/draw 33 n // 10.0 / 5
                    ]
                    down [face/state: on]
                    alt-down [face/state: on]
                    up [if face/state [do-face face face/text] face/state: off]
                    alt-up [if face/state [do-face-alt face face/text] face/state: off]
                    over [face/state: on]
                    away [face/state: off]
                ]
                cue face action
                show face
            ]
        ]
    ]
]
module-stack: head module-stack
~p1/pane: module-stack/3/gui
~p2/pane: module-stack/4/gui
~p3/pane: module-stack/5/gui
~p4/pane: module-stack/1/gui
~chk-sync/data: false
module-stack/3/~name/effect: [merge gradmul 0x1 black 127.127.127 draw [line-width 0.5 pen white fill-pen 132.255.250 font ~fnt-h3 rotate -90 translate -47x-2 text 0x0 "DCO1" vectorial]]
module-stack/4/~name/effect: [merge gradmul 0x1 black 127.127.127 draw [line-width 0.5 pen white fill-pen 132.255.250 font ~fnt-h3 rotate -90 translate -47x-2 text 0x0 "DCO2" vectorial]]
clipboard-envelope: copy [rates [] levels []]
melody-parser: does [
    ot: now/time/precise
    parse melody [
        some [
            set time tuple! ()
            any [
                set nt string! (note: nt)
                | set nt 'none (note: none)
                | set param word! set value number! ()
            ]
        ]
    ]
    ~t2/text: now/time/precise - ot show ~t2
]
view center-face synth