% Content-encoding: UTF-8
\documentclass[ngerman]{article}
\usepackage[utf8]{inputenc}
\usepackage{multicol}
% \usepackage{babel}
\usepackage{xspace}
\setcounter{secnumdepth}{0}
\setcounter{tocdepth}{0}
\usepackage{german}

%\newcommand{\code}[1]{\texttt{#1}}
%\newcommand{\ret}{\textsf{$<$ret$>$}\xspace}
%\newcommand{\ret}{$\hookleftarrow$\xspace}

\renewcommand{\reftextbefore}{auf der vorherigen Seite}
\renewcommand{\reftextfacebefore}{auf der gegenüberliegenden Seite}
\renewcommand{\reftextafter}{auf der nächsten Seite}
\renewcommand{\reftextfaceafter}{auf der gegenüberliegenden Seite}
\renewcommand{\reftextfaraway}[1]{auf Seite \pageref{#1}}
%\renewcommand{\figurename}{Listing}

\title{Wave Engine (2)}
\ifx\shorttitle\undefined\else
%\shorttitle{WE(2)} % der Titel ist kurz genug!
\fi
\author{Hannes Teich} 


\begin{document}

\maketitle

\begin{abstract}
Weiter geht's mit der virtuellen „viermanualigen“ PC-Orgel, die mit Hilfe eines Programms in Gforth eine ASCII-Partitur in eine Wave-Datei wandelt. In der letzten Folge wurden vorgestellt: die Grundfunktion des Sinus-Generators, der Aufbau der Wave-Datei sowie die Interpretation von Notenzeilen. Diesmal wenden wir uns der Registrierung und dem Datentransport zu. Zunächst eine grobe Übersicht „über alles“.
\end{abstract}

%Abbildung 1: Vereinfachtes Schema der „Wave Engine“
\begin{figure*}[b]
\begin{center}
\includegraphics[width=0.8\textwidth]{2012-01/Teich-Bild1WE2}
%\includegraphics{2012-01/Teich-Bild1WE2}
\caption{\label{bild1}Vereinfachtes Schema der Wave Engine}
\end{center}
\end{figure*}

\begin{multicols}{2}
Die Beschickung des Sinus-Generators ist, wie \textbf{Abb. \ref{bild1}} zeigt, ziemlich üppig. Ursprünglich hatte ich mit der \textit{Wave Engine} (Kosename: \textit{Waver}) nur ein Werkzeug zum Experimentieren mit reiner Stimmung im Sinn. Der Riesen-Aufwand wäre hierfür nicht nötig gewesen, aber ich konnte ihn mir einfach nicht verkneifen. :-)

Es sind zwar noch nicht alle Funktionen am Laufen, aber das Konzept steht soweit und wird sich – durch diese Artikelserie gedopt – hoffentlich bald akustisch in voller Pracht präsentieren können. Die Regeln für die Partitur können jedenfalls jetzt schon beschrieben werden. Was bislang akustisch geht, zeigen die Links am Ende.

In der Partitur stecken die Vorgaben für alle Aktionen, als da sind: Feste und veränderbare Voreinstellungen, die Registrierung, die Musiknoten sowie Kommentare, die teilweise in die Wave-Datei übernommen werden. Die Registrierung kann direkt erfolgen oder in Records abgelegt werden, von wo die Inhalte bei Bedarf durch \textit{Habitus}-Marken abgeholt werden, die ins Notenbild eingestreut sind, ohne dieses unnötig aufzublähen. Hinter solchen Marken kann sich sehr viel Registrierung verbergen.

Die Notenzeilen für die vier virtuellen \textit{Manuale} werden nacheinander gelesen, sollen aber zugleich erklingen. Das macht einen \textit{FIFO}-Pufferspeicher nötig, aus dem der \textit{Loader} jeweils das zum \textit{Generator} schickt, was dieser gerade benötigt. Der dem Interpreter nachgeschaltete Collector sorgt dafür, dass die Daten im rechten Format an den Pufferspeicher übergeben werden.

\section{Die Funktionen im Einzelnen, grob beleuchtet}
\begin{itemize}
\item \textbf{tuning} – die Stimmungshöhe, normalerweise 440 Hertz für das „eingestrichene A“.
\item \textbf{pitch} und \textbf{duration} – Höhe und Dauer eines Tons.
\item \textbf{habitus} – Marken, durch welche die „Records“ angezapft werden.
\item \textbf{tempo} – Anzahl Viertelnoten pro Minute.
\item \textbf{ritard} – langsamer werden (ritardando). Negativ: schneller.
\item \textbf{volume} – Partialtöne einzeln und alle gemeinsam.
\item \textbf{accent} – Lautstärke für einen oder mehrere Töne erhöhen/erniedrigen.
\item \textbf{crescendo} – anschwellende Lautstärke. Negativ: abschwellen.
\item \textbf{fading}, \textbf{sustain}, \textbf{staccato} – Abklingen, Nachklingen, Zwischenpausen.
\item \textbf{mellow} – weicher Toneinsatz.
\item \textbf{transpo} – Transponierung in andere Tonarten.
\item \textbf{drift} – Verstimmung, z. B. zur Erzeugung der Naturseptime.
\item \textbf{bender} – Ziehen der Tonhöhe.
\item \textbf{angle} – Richtung der Tonquelle (stereo).
\item \textbf{vibrato}, \textbf{tremolo}, \textbf{beat} – Frequenz-, Amplituden-, Phasenvibrato.
\item \textbf{echo} – leiser werdendes Flatterecho. (Etwas dürftiger Ersatz für Hall; braucht viel Speicher.)
\item \textbf{arpeggio} – gebrochene Akkorde.
\item \textbf{formants} – von Tonhöhe unabhängige Vokale. Durch Berechnung der Obertöne erzeugt. Manche Vokale haben zwei Maxima, deshalb „doppelt“.
\item \textbf{wahwah} – wechselnde Klangfarbe, etwa wie Trompete mit vorgehaltenem Hütchen. (Durch Überblendung zwischen zwei Vokalen erzeugt.)
\end{itemize}

Was fehlt noch? Außer Hall wüsste ich nichts mehr. Hall zu berechnen übersteigt meine Fähigkeiten. Immerhin kann man das Wave-Ergebnis nachträglich durch eine Hallmaschine laufen lassen. Oder man begnügt sich mit Flatterecho. (\textit{Les Paul} hat davon viel Gebrauch gemacht.) \textbf{vibrato} und \textbf{tremolo} unterstütze ich nur halbherzig, die Funktion \textbf{beat} reicht mir eigentlich. Sie klingt recht gut, etwa wie beim Vibraphon von \textit{Milt Jackson}.

Bei einer Pfeifenorgel werden die Registerknöpfe gezogen, ehe Manuale und Pedal mani- und pedipuliert werden. Beim \textit{Waver} wird die Registrierung durch die Partitur vorgenommen. In beiden Fällen muss eine „Apparatur“ hinter den Kulissen für die Verwirklichung sorgen.

Folgende Funktionen aus obiger Palette waren für das Tonbeispiel \textit{Top One} im Einsatz: \textbf{tuning, pitch, duration, tempo, volume, fading, sustain, staccato}. Schmerzlich vermisst wurden die Funktionen \textbf{mellow} und \textbf{bender}.

\section{Weicher Toneinsatz im Prinzip}
%Abbildung 2: Bildung des weichen Toneinsatzes
\begin{figure}
\begin{center}
\includegraphics[width=1.0\textwidth]{2012-01/Teich-Bild2WE2}
%\includegraphics{2012-01/Teich-Bild2WE2}
\caption{\label{bild2}Bildung des weichen Toneinsatzes}
\end{center}
\end{figure}

Die \textbf{Abb.\ref{bild2}} zeigt, wie ich mir die Generierung des weichen Toneinsatzes (\textbf{mellow}) vorstelle. Oben ist die Hüllkurve eines „gezupften“, abklingenden Tons zu sehen, wie er beim Thema \textit{Generator} beschrieben worden ist: Die Amplitude wird laufend mit einem Faktor „kleiner Eins“ multipliziert, was zu konstanten Halbwertszeiten führt. Die Kurve darunter entsteht auf gleiche Weise, nur mit stärkerer Dämpfung und mit vorgegebener Anfangsamplitude von (sagen wir) 100 Einheiten.
Die dritte ist die auf den Kopf gestellte zweite Kurve, was durch Subtraktion von 100 bewerkstelligt wird. Außerdem wird durch 100 dividiert, damit sie als Dämpfungsfaktor dienen kann. Mit diesen Faktor-Werten wird die Ausgangskurve multipliziert, und – voila! – das Resultat kann sich sehen (und hören) lassen.

\section{Implementieren des weichen Toneinsatzes}
Wir betrachten nur \textit{Manual A}, denn die vier \textit{Manuale} sind gleich und selbständig. Jedes \textit{Manual} hat sechs Stimmen, jede Stimme hat drei Teiltonreihen, und jede dieser Reihen hat 16 Teiltöne. Man könnte den gewünschten Effekt jeder Stimme zuordnen (eine Einstellung für alle Stimmen), aber ich meine, die Teiltonreihen sollten separat behandelt werden können (also drei Werte für alle sechs Stimmen). 

\begin{verbatim}

    regA{ mellow=50,50,50; }
    regA{ *1 mellow=50,50,50; }

\end{verbatim}

Hier sind zwei Varianten einer \textbf{mellow}-Registeranweisung zu sehen: Für die drei Teiltonreihen des \textit{Manuals} A ist ein Dämpfungswert von 50 angegeben. Die erste Variante kann überall zwischen Notenzeilen auftauchen. Die zweite speichert die Vorgabe als \textit{Habitus} in einem Records-Pool, von wo sie durch Angabe von \texttt{*1} innerhalb einer Notenzeile geholt werden kann, um sogleich zu wirken.
In solch einer Register-Zeile können mehrere (oder alle) Registeranweisungen enthalten sein. Auf die Interpretation von Partiturzeilen will ich an dieser Stelle aber nicht eingehen (die Syntax gibt’s paar Seiten weiter unten), sondern die \textbf{mellow}-Funktion als solche betrachten.
Die Dämpfungskurve in \textbf{Abb. 2} reiht sich zwanglos in bereits vorhandene Dämpfungen (\textbf{fading}, \textbf{sustain}) ein. Einst waren die Vorgaben noch nicht in der Partitur, sondern Teil des Programms, was sehr hinderlich war. Der Anschaulichkeit halber greife ich aber gern auf diesen Zustand zurück.

\begin{verbatim}

: adjust  ( u - r)  0 max 3000 min
                    ?dup IF drop 3000 THEN
                    draft_ IF 4 * THEN
                    1e ( u) s>f 1e5 f/ 1e f+ f/ ;

       5  adjust  0 :fade-A* f!    \ fading
      18  adjust  1 :fade-A* f!
      12  adjust  2 :fade-A* f!

      50  adjust  0 :sust-A* f!    \ sustain
      50  adjust  1 :sust-A* f!
      50  adjust  2 :sust-A* f!

      50  adjust  0 :mellow-A* f!  \ mellow
      50  adjust  1 :mellow-A* f!
      50  adjust  2 :mellow-A* f!

\end{verbatim}

Die Funktion \texttt{adjust} ist nötig, um für die Vorgaben handliche Werte zu kriegen. Sie macht das durch die Formel: \texttt{r = 1/(1+u/100000)}. Beispiele:

\begin{verbatim}

       1 adjust f. 0.99999  ok 
       5 adjust f. 0.99995  ok 
      10 adjust f. 0.99990  ok 
      50 adjust f. 0.99950  ok 
     100 adjust f. 0.99900  ok
     500 adjust f. 0.99502  ok 
    1000 adjust f. 0.99010  ok 
    3000 adjust f. 0.97087  ok
       0 adjust f. 0.97087  ok

\end{verbatim}

Wert 1 liefert die geringste Dämpfung. Für den weichen Toneinsatz heißt das, dass der Ton lange braucht, um seinen Nennwert zu erreichen. Dagegen unterdrückt das Maximum 3000 (oder auch 0) den \textbf{mellow}-Effekt hinreichend und lässt den Toneinsatz „knallen“ (wie in \textit{Top One}).

\texttt{:mellow-A*} bezeichnet ein Array-Element, wie es in der letzten Artikelfolge beschrieben worden ist:

\begin{verbatim}

create mellow-A*|  3 8 *  allot  ( 3 cells)
: :mellow-A* ( cell# -- addr) 8 * mellow-A*| + ;

\end{verbatim}

Hierbei handelt es sich um die Speicherung der Vorgaben. Nun brauchen wir noch die Arrays für die beiden Steuerkurven aus \textbf{Abb. \ref{bild2}}. Diese müssen für jede der sechs Stimmen vorhanden sein, also für 18 Teiltonreihen:

\begin{verbatim}
create dflat-A*|  18 8 *  allot  ( 18 cells)
: :dflat-A* ( cell# -- addr) 8 * dflat-A*| + ;

create mel-A*|  18 8 *  allot  ( 18 cells)
: :mel-A* ( cell# -- addr) 8 * mel-A*| + ;

\end{verbatim}

Immer wenn ein neuer Ton beginnt, werden (in der Routine \texttt{latch>gen-A}, hier nicht dargestellt) drei Array-Elemente mit \texttt{100e} geladen:

\begin{verbatim}

  3 0  DO  100e i :dflat-A* f!  LOOP

\end{verbatim}

Dann geht’s weiter zur Berechnung der Faktorkurve:

\begin{verbatim}

: mellow-A ( --) 
    r/m_ :dflat-A* f@      \ deflate 100 -> zero 
    r/v_ :mellow-A* f@ f*  \ damping value 
    r/m_ :dflat-A* fdup f! \ update dflat
         100e fswap f-     \ upside down
         100e f/           \ make it <1
    r/m_ :mel-A* f! ;      \ mellow factor

\end{verbatim}

Aufgerufen wird dieses Wort durch folgende bereits bekannte, nun aber aufgebohrte Routine:

\begin{verbatim}

\            <<< TONGENERATOR (A) >>>
: waver-A  ( --)
         empty-accu-A  \ AKKU LEEREN
  v/m 0 DO   i to v/m_ \   6 voices per manual
  r/v 0  DO  i to r/v_ \   3 p-rows per voice
  p/r 0   DO i to p/r_ \  16 partials per p-row
\ r/m                  \  18 p-rows per manual
\ p/m                  \ 288 partials per manual
                  v/m_ r/v * r/v_ + to r/m_
                  v/m_ p/v * r/v_ p/r * +
                             p/r_ + to p/m_
               sinus-A        \ KURVENBERECHNUNG
               fadings-A      \ ABKLINGVORGÄNGE
          LOOP mellow-A       \ WEICHER EINSATZ
         LOOP  phase-incr-A   \ PHASE INKREMENT
        LOOP   timer-decr-A   \ TIMER DEKREMENT
               store-accu-A ; \ AKKU UPDATE

\end{verbatim}

Man sieht: Kurvenberechnung und Abklingvorgänge werden für jeden der 288 Teiltöne des \textit{Manuals} aufgerufen, der weiche Toneinsatz dagegen nur für die 18 Teiltonreihen.

Die Abklingvorgänge sind von der Erweiterung nicht betroffen, wohl aber die Kurvenberechnung. Darin taucht nun \texttt{:mel-A*} als Faktor auf und verkleinert somit die Amplitude während des Tonanfangs:

\begin{verbatim}

\            <<< KURVENBERECHNUNG (A) >>> 
: sinus-A  ( --) 
  p/m_ :ampl-A* f@ f0> \ avoid multiply by zero 
IF 
  v/m_ :phase-AL* f@   \ root's phase (L) 
  p/r_ 1+ s>f f*       \ multiply by partial# 
         fsin          \ sinus (L) 
  p/m_ :ampl-A* f@ f*  \ multiply by ampl 
  r/m_ :mel-A* f@ f*   \ multiply by mellow 
         accu-AL* f+!  \ update accu (L) 

         stereo_    IF \ second channel (R) 
  v/m_ :phase-AR* f@   \ root's phase (R) 
  p/r_ 1+ s>f f*       \ multiply by partial# 
         fsin          \ sinus (R) 
  p/m_ :ampl-A* f@ f*  \ multiply by ampl 
  r/m_ :mel-A* f@ f*   \ multiply by mellow 
         accu-AR* f+!  \ update accu (R) 
                    THEN 
THEN ;

\end{verbatim}

Mit diesen Ergänzungen sind bei passender Vorwahl sanfte Einschwinger zu hören:

%Abbildung 3: harte und weiche Einschwingvorgänge
\begin{figure}
\begin{center}
\includegraphics[width=1.0\textwidth]{2012-01/Teich-Bild3WE2}
%\includegraphics{2012-01/Teich-Bild3WE2}
\caption{\label{bild3}Harte und weiche Einschwingvorgänge}
\end{center}
\end{figure}

Wird dann noch das Fading abgeschaltet, fehlt für ordentliche Orgeltöne nur noch das Anblasgeräusch.

\section{Ziehen der Tonhöhe im Prinzip}
Der zweite unerfüllte Wunsch beim Verfassen von \textit{Top One} war die \textbf{bender}-Funktion, das Ziehen der Tonhöhe. (\textit{Les Paul} ist ohne diesen Effekt gar nicht vorstellbar.)

Ich werde mich für diesmal auf die schematischen Zeichnungen beschränken, denn ich möchte noch ein weiteres Thema unterbringen, damit das Gesamtbild der Wave Engine Gestalt annimmt.

Die \textbf{bender}-Funktion ist aufwändiger als der weiche Toneinsatz. Ein kurzes Hochziehen zur Nenn-Tonhöhe reicht nicht, denn es ist auch so etwas wie „in die Knie gehen“ erwünscht, also ein Absenken und langsame Rückkehr zur Nenn-Tonhöhe.

%Abbildung 4: „Magische Punkte“ à la Bézier
\begin{figure}
\begin{center}
\includegraphics[width=1.0\textwidth]{2012-01/Teich-Bild4WE2}
%\includegraphics{2012-01/Teich-Bild4WE2}
\caption{\label{bild4}„Magische Punkte“ à la Bézier}
\end{center}
\end{figure}

\textbf{Abb. \ref{bild4} }zeigt eine Bézier-Kurve (zur Konstruktion siehe Wikipedia) mit der gewünschten Form. Leider fehlt hier eine Zeit-Achse; die Kurve müsste zwischengespeichert werden, um sie für den zeitlichen Verlauf zu nutzen, was einen unnötigen Speicherbedarf zur Folge hätte.

Es muss auch anders gehen. Die abklingende Kurve aus \textbf{Abb. \ref{bild2}} strebt nach der Nulllinie. Was wir hier brauchen, ist ein Streben nach vorgegebenen Werten (wie die Rechteckkurve in \textbf{Abb. \ref{bild5}}).

%Abbildung 5: Kurve folgt (träge) den Vorgaben
\begin{figure}
\begin{center}
\includegraphics[width=1.0\textwidth]{2012-01/Teich-Bild5WE2}
%\includegraphics{2012-01/Teich-Bild5WE2}
\caption{\label{bild5}Kurve folgt (träge) den Vorgaben}
\end{center}
\end{figure}

\textbf{Abb. \ref{bild5}} zeigt die Funktion: Die Kurve bemüht sich mit der ihr eigenen Trägheit, den Vorgaben zu folgen. Mit geeigneteren Vorgaben verschwinden die Knicke, \textbf{Abb. \ref{bild6}}:

%Abbildung 6: Die gewünschte Tonhöhen-Kurve
\begin{figure}
\begin{center}
\includegraphics[width=1.0\textwidth]{2012-01/Teich-Bild6WE2}
%\includegraphics{2012-01/Teich-Bild5WE2}
\caption{\label{bild6}Die gewünschte Tonhöhen-Kurve}
\end{center}
\end{figure}

Diese Kurve muss nur noch dem Sinus-Generator angedient werden. (Dazu später mehr.)

\section{Draft-Betriebsart}
Man könnte vielleicht einwenden, dass der viele Aufwand den \textit{Waver} unnötig träge werden lässt. Aber da er von vornherein nicht für Echtzeit konzipiert war, also keinen Klaviatur-Anschluss vorsieht, geht es allenfalls um das Warten aufs Ergebnis nach Programmstart. Hier kann durch eine Draft-Betriebsart viel Zeit eingespart werden. Wie schon aus der Wave-Datei ersichtlich, kann die Auflösung von 44 auf 11 Kiloframes pro Sekunde gedrosselt werden. Weiter kann auf die Berechnung höherer Teiltöne verzichtet werden sowie auf eine Reihe von Funktionen wie \textbf{mellow} und anderes. Es geht in aller Regel hauptsächlich darum, Fehler in der Abfolge der Noten aufzudecken. Rhythmisch heikle Stellen können zudem leicht für sich getestet werden, was nur wenig Zeit kostet.
\end{multicols}

\section{Register-Syntax und Erkennung}

\begin{verbatim}

\ === Partiturzeilen-Typen ===
\   fix{ ... }      ` Vorgaben (fest)
\   set{ ... }      ` Einstellungen (veränderbar)
\  regA{ ... }      ` Registrierung Manual A (ebenso B, C, D)
\     A{ ... }      ` Notenzeile für Manual A (ebenso B, C, D)
\     [< ... >]     ` Titelzeile: wird in die Wave-Datei übernommen
\     [[ ... ]]     ` Kommentar (auch mehrzeilig), blendet Code aus
\    [[[ ... ]]]    ` Kommentar (kann [[...]] beinhalten)
\
\ === Feste Vorgaben ===
\   fix{ dra=... } draft             fix{ ste=... } stereo
\
\ === Veränderbare Einstellungen ===
\   set{ man=... } manuals           set{ tem=... } tempo
\   set{ mic=... } micro             set{ tun=... } tuning
\
\ === Registrier-Anweisungen (alphabetisch) ===
\  regA{ acc=... } accent (2)       regA{ pa1=... } partials1 (16)
\  regA{ ang=... } angle (1)        regA{ pa2=... } partials2 (16)
\  regA{ arp=... } arpeggio (1)     regA{ pa3=... } partials3 (16)
\  regA{ bea=... } beat (1)         regA{ pre=... } preset (1)
\  regA{ ben=... } bender (12)      regA{ rit=... } ritard (2)
\  regA{ cre=... } cescendo (2)     regA{ sta=... } staccato (2)
\  regA{ dri=... } drift (2)        regA{ sus=... } sustain (3)
\  regA{ ech=... } echo (2)         regA{ tre=... } tremolo (2)
\  regA{ fad=... } fade (3)         regA{ tra=... } transpo (1)
\  regA{ fo1=... } formant1 (6)     regA{ vib=... } vibrato (2)
\  regA{ fo2=... } formant2 (6)     regA{ vol=... } volume (1)
\  regA{ mel=... } mellow (3)       regA{ wah=... } wahwah (4)
\
\ === Aufbau der Records ===
\    0  pa1 --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
\   16  pa2 --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
\   32  pa3 --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
\   48  fad --- --- sus --- --- mel --- --- sta --- tra acc --- cre ---
\   64  fo1 --- --- --- --- --- fo2 --- --- --- --- --- wah --- --- ---
\   80  ben --- --- --- --- --- --- --- --- --- --- --- ech --- dri ---
\   96  vib --- tre --- bea ang arp rit --- vol pre

\ === Offsets für den Zugriff in den Records ===
  00 constant pa1    16 constant pa2    32 constant pa3    48 constant fad
  51 constant sus    54 constant mel    57 constant sta    59 constant tra
  60 constant acc    62 constant cre    64 constant fo1    70 constant fo2
  76 constant wah    80 constant ben    92 constant ech    94 constant dri
  96 constant vib    98 constant tre   100 constant bea   101 constant ang
 102 constant arp   103 constant rit   105 constant vol   106 constant pre

\ Beispiel: Aktion für Register-Kommando "echo=" (noch nicht implementiert)
: ech:  ( a u -- a' u' f)  cr	." ### echo= ###"  true ;

\ === Zulässige Register-Kommandos ===
\ Hinweis: Hier reicht die voreingestellte Stacktiefe nicht ganz aus.
: regsym$          ( regular)                     ( short)
        ['] pa1:   c" partials="       ['] pa1:   c" par="
        ['] pa1:   c" partials1="      ['] pa1:   c" pa1="
        ['] pa2:   c" partials2="      ['] pa2:   c" pa2="
        ['] pa3:   c" partials3="      ['] pa3:   c" pa3="
        ['] fad:   c" fading="         ['] fad:   c" fad="
        ['] sus:   c" sustain="        ['] sus:   c" sus="
        ['] mel:   c" mellow="         ['] mel:   c" mel="
        ['] sta:   c" staccato="       ['] sta:   c" sta="
        ['] tra:   c" transpo="        ['] tra:   c" tra="
        ['] acc:   c" accent="         ['] acc:   c" acc="
        ['] cre:   c" crescendo="      ['] cre:   c" cre="
        ['] fo1:   c" formants="       ['] fo1:   c" for="
        ['] fo1:   c" formant1="       ['] fo1:   c" fo1="
        ['] fo2:   c" formant2="       ['] fo2:   c" fo2="
        ['] wah:   c" wahwah="         ['] wah:   c" wah="
        ['] ben:   c" bender="         ['] ben:   c" ben="
        ['] ech:   c" echo="           ['] ech:   c" ech="
        ['] dri:   c" drift="          ['] dri:   c" dri="
        ['] vib:   c" vibrato="        ['] vib:   c" vib="
        ['] tre:   c" tremolo="        ['] tre:   c" tre="
        ['] bea:   c" beat="           ['] bea:   c" bea="
        ['] ang:   c" angle="          ['] ang:   c" ang="
        ['] arp:   c" arpeggio="       ['] arp:   c" arp="
        ['] rit:   c" ritard="         ['] rit:   c" rit="
        ['] vol:   c" volume="         ['] vol:   c" vol="
        ['] pre:   c" pre="            ['] pre:   c" preset="
        ['] reg_dummybar  c" |"        ['] rem:   c" `" ( skip line)
        ['] reg_brace-right  c" }" ;

create regsym regsym$
            , ,  , ,  , ,  , ,  , ,     , ,  , ,  , ,  , ,  , ,  ( 10)
            , ,  , ,  , ,  , ,  , ,     , ,  , ,  , ,  , ,  , ,  ( 20)
            , ,  , ,  , ,  , ,  , ,     , ,  , ,  , ,  , ,  , ,  ( 30)
            , ,  , ,  , ,  , ,  , ,     , ,  , ,  , ,  , ,  , ,  ( 40)
            , ,  , ,  , ,  , ,  , ,     , ,  , ,  , ,  , ,  , ,  ( 50)
            , ,  , ,  , ,  , ,  , ,     0 ,  ( sentinel)

\ Beispiel für den Zugriff:
\ s" beat=123" regsym extract-token ( string count token flag)
\ .s <4> 161889205 7 3054668 -1  ok
\ "3054668 PERFORM" führt die gewünschte "beat"-Handlung aus.
\ "161889205 7 type" gibt die restliche Zeichenkette ("123") aus.
\ Wird kein Symbol gefunden (flag=0), wird die ganze Kette ausgegeben.

\ === Partitur: Komplette Registrierung für Record 1 ===
\     Auswahl und Reihenfolge beliebig.
\     Alle Null-Angaben durch "preset=0" überflüssig.

regA{ *1 partials=
      (1:5000  2:   0  3:   0  4:   0  5:   0  6:   0  7:   0  8:   0
       9:   0 10:   0 11:   0 12:   0 13:   0 14:   0 15:   0 16:   0),
      (1:3000  2:1000  3: 500  4: 300  5: 200  5: 100  7:   0  8:   0
       9:   0 10:   0 11:   0 12:   0 13:   0 14:   0 15:   0 16:   0),
      (1:   0  2:   0  3:1200  4: 500  5:   0  6: 400  7:   0  8: 200
       9:   0  10:  0 11:   0 12:   0 13:   0 14:   0 15:   0 16:   0);
      fading=8,20,40;                 ` 3 x Abklingen
      sustain=400,400,400;            ` 3 x Nachklingen
      mellow=0,0,0;                   ` 3 x weicher Einsatz
      staccato=1000,100;              ` Dämpfung, Vorlaufzeit
      transpo=0;                      ` Tonart-Transponierung
      accent=115,3;                   ` Lautstärke in Prozent, Töneanzahl
      crescendo=0,0;                  ` Zeit, Wert
      formant1=0,0,0,0,0,0;           ` 2 x Frequenz, Amplitude, Breite
      formant2=0,0,0,0 0 0;           ` 2 x Frequenz, Amplitude, Breite
      wahwah=0,0,0,0;                 ` 1|2, Affinität, Steilheit, Dots
      bender=0,0,0,0,0,0,0,0,0,0,0,0  ` Anfangswert, Affinität, xy-Paare
      echo=0,0;                       ` Zeit, Wert
      drift=0,0;                      ` Verstimmung des Manuals
      vibrato=0,0;                    ` Frequenzvibrato
      tremolo=0,0;                    ` Amplitudenvibrato
      beat=0;                         ` Phasenvibrato (links-reechts)
      angle=0;                        ` Stereo-Position
      arpeggio=0;                     ` Akkordbrechung (Dauer)
      ritard=0,0;                     ` langsamer werden (ritardando)
      volume=0;                       ` Lautstärke-Korrektur (0=neutral)
      preset=0;                       ` Voreinstellung (zuerst gelesen)
}

\ === Partitur: Registrierung der Teiltöne wie oben in Kurzschreibweise ===

regA{ *2 preset=0;
         par=(5000),(3000 1000 500 300 200 100),(3:1200 500 6:400 8:200); }

\end{verbatim}
\begin{multicols}{2}

\section{Von der Partitur zum Generator}
Der Weg von den Musiknoten bis zur Generierung der Töne ist nicht ganz trivial. Um für diesmal bis zum \textit{Generator} vorzudringen, gehe ich in der Beschreibung vom Ergebnis des Partitur-Interpreters aus, sonst verheddern wir uns in den Interpunktionen.

Ich spreche jetzt nicht von den zahlreichen Einstellungen und Registrierungen, sondern nur von \textit{Tonhöhe}, \textit{Tondauer} und \textit{Habitus}. Das sind die drei Datentypen, die gepuffert werden müssen, weil sie im \textit{Generator} für vier \textit{Manuale} zugleich bereit stehen müssen, obgleich sie in der Partitur über mehrere Zeilen verteilt sind.

Der dem Interpreter nachgeschaltete \textit{Collector} packt die Daten in das Format, in dem sie durch den Pufferspeicher transportiert werden. Eine Speicherebene enthält 8 Halbzellen. Ich habe ein (nicht standardisiertes) 16-bit-Format gewählt. Mit geringerem Speicherbedarf kann ich das kaum begründen. In meiner beruflichen „Karriere“ aber war er fast immer ein Thema, und das hängt mir halt noch nach.

\section{Den \textit{FIFO}-Speicher laden}
Mit den Befehlen\texttt{ habit!}, \texttt{durat!} und \texttt{pitch!} werden die Daten im \textit{Collector} gesammelt und mit \texttt{coll>fifo} zum \textit{FIFO} gesandt. \textit{FIFO} („first in – first out“) bedeutet, dass es um einen Ringspeicher geht, der ständig bereit ist, Daten anzunehmen und abzugeben, so lange es nicht zu overflow oder underflow kommt.

\begin{verbatim}

0 value coll-N_    \ address of actual collector

\ Load actual collector with habit
: habit!    ( n --)
    dup 31 > IF |HABIT!| THEN  /error
                $001F and  ( 5 bit)
                coll-N_ w@ $FFE0 and or
                coll-N_ w! ;

\ Load actual collector with durat
: durat!    ( n --)  coll-N_ 2 + w! ;

\ Load actual collector with pitch
: pitch!    ( n --) 
           bracketcount @ 0=      \ [accord]? 
    IF    coll-N_ cell+ 12 0 fill \ no
          1 voice# !              \ choose voc #1 
    ELSE  voice# @ 5 >            \ [accord ]
                 IF |VOICE| THEN  \ too many vocs 
    THEN  coll-N_ cell+     \ skip habit & durat 
          voice# @ 1 2* + w!      \ write pitch 
          bracketcount @          \ [accord]?
    IF    voice# incr             \ next voice
    THEN ; 

\ Find counter address (given fifo address)
: >cnt-in    ( fifo-a -- in-a  )      ;
: >cnt-lock  ( fifo-a -- lock-a)  2 + ;
: >cnt-out   ( fifo-a -- out-a )  4 + ;

\ Find plain address due to in-counter
: >man-in  ( fifo-a -- plain-a)
    dup  >cnt-in @  no-of-plains mod ( a plain#)
    swap  man-plain ;                ( plain-a)

\ Transfer data from coll-A to fifo-A
: coll>fifo-A  ( --)
    fifo-A >cnt-in w@  fifo-A >cnt-lock w@ -
    nr-of-plains >=  IF  |FIFO-OFL|  THEN
    coll-A fifo-A >man-in 16 cmove \ transfer
    fifo-A >cnt-in incr ;

\end{verbatim}

\section{Adressierung des FIFO-Ringspeichers}
Im Kopf jedes der vier (den \textit{Manualen} zugeordneten) Pufferspeicher sind drei Zähler untergebracht (\textbf{Abb. \ref{bild8}}), welche die Adressierung der aktuellen 16-bit-Pufferebenen übernehmen.\texttt{ cnt-in} dient als Zeiger auf die zuletzt beschriebene Ebene, \texttt{cnt-out }als Zeiger auf die nächst zu lesende Ebene, und \texttt{cnt-lock} verhindert, dass im Falle von Wiederholungsschleifen noch gebrauchte Daten überschrieben werden. Dabei ist zu beachten: \texttt{cnt-out} darf \texttt{cnt-in} nicht überholen (underflow), \texttt{cnt-in} darf \texttt{cnt-lock} nicht überholen (overflow); \texttt{cnt-lock} folgt normalerweise \texttt{cnt-out}, bleibt jedoch bei Schleifenbildung am Schleifenanfang hängen.

Das mag soweit plausibel klingen, aber wer überholt in einem Ringpuffer wen? Wer ist vorn und wer ist hinten? Um hier klare Verhältnisse zu bekommen, laufen die Zähler linear von 0 bis maximal 65535 und werden erst „modulo Puffertiefe“ zu Zeigern. Gezählt werden „Töne“, und dazu dürfte die Zählerkapazität reichen.

\textbf{Abb. \ref{bild7}} stellt beispielhaft einen Ringpuffer mit 10 Ebenen Tiefe dar. Gezählt wird zwar fortlaufend, aber adressiert werden nur die 10 Ebenen. Hier ist ersichtlich, dass \texttt{cnt-in} auf der Position von \texttt{cnt-lock} angekommen ist: Overflow! Man sieht: \texttt{cnt-in} darf \texttt{cnt-lock} nur maximal um die Puffertiefe vorauseilen, und obiger Satz muss heißen: \texttt{cnt-in} darf \texttt{cnt-lock} \textit{plus Puffertiefe} nicht überholen.

\begin{center}
\begin{verbatim}

    x-- "in" kleiner "lock" + Puffertiefe --x
                x---- "out" kleiner "in" ---x
    |lock       |out                        |in
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 |
|---|---|---|---|---|---|---|---|---|---|---|---|
x------- Puffertiefe: 10 Ebenen --------x

\end{verbatim}
\caption{\label{bild7}Overflow! „in“ hat „lock“ eingeholt.}
\end{center}

\section{Der Weg durch den Pufferspeicher}
\textbf{Abb. \ref{bild8}} zeigt den Weg der Daten vom \textit{Collector} über den Pufferspeicher zum \textit{Loader} (siehe \textbf{Abb. \ref{bild1}}). Letzterer (für jedes \textit{Manual} separat vorhanden) versorgt den \textit{Generator} direkt, so dass dieser auf die Daten aller beteiligten \textit{Manuale} zugleich zugreifen kann. Dem \textit{Loader} ist ein \textit{Latch} (Zwischenpuffer) vorgeschaltet. Das ist erforderlich, damit während der aktuellen Generierung der Töne getestet werden kann, ob als nächstes ein Ton (Akkord) folgt oder eine Pause. Das entscheidet über die Art der Dämpfung (\textit{Staccato} oder \textit{Sustain}). Bei \textit{Staccato} wird der aktuelle Ton verkürzt. (\textbf{Abb. \ref{bild8}} zeigt nur das \textit{Latch}; der eigentliche \textit{Loader} ist von gleicher Art und nachgeschaltet zu denken.)

\begin{center}
\begin{verbatim}

                          FIFO
                      .----------. 
                      | cnt-in   | 
                      | cnt-lock |      Loader
        Collector     | cnt-out  |      (Latch) 
        .--------.    |----------|    .--------. 
 habit! | habit  | -> |  habit   | -> | habit  | 
 durat! | durat  | -> |  durat   | -> | durat  | 
 pitch! | pitch1 | -> |  pitch1  | -> | pitch1 | 
        | pitch2 | -> |  pitch2  | -> | pitch2 | 
        | pitch3 | -> |  pitch3  | -> | pitch3 | 
        | pitch4 | -> |  pitch4  | -> | pitch4 | 
        | pitch5 | -> |  pitch5  | -> | pitch5 | 
        | pitch6 | -> |  pitch6  | -> | pitch6 | 
        '--------'    |----------|    '--------' 
        width=16bit   |  habit   |   width=16bit
                      |  durat   | 
                      |  pitch1  |

\end{verbatim}
\caption{\label{bild8}Daten passieren den \textit{FIFO}-Speicher}
\end{center}

Aus \textbf{Abb. \ref{bild9}} ist die Anordnung der transportierten Daten in acht 16-bit-Halbzellen ersichtlich. Die sechs Werte für die \textit{Tonhöhe} (pitch1 bis pitch6) bewegen sich im Bereich 1 bis 478 (plus 0 für Pause und -1 für Tonverlängerung). Die \textit{Tondauer} (durat) einer Viertelnote hat den Wert 720, der Maximalwert von 64800 entspricht 90 Viertelnoten oder 22,5 Vierviertel- oder 30 Dreivierteltakten.

\begin{verbatim}

                 byte 1          byte 0
              (loop count)   (end)  (habit)
            |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
    habit:  |    2...255    | >>> |  0...31 |
            |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+

            |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
    durat:  |           1...64800           |
            |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+

            |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
   pitch1:  |s|         -1...478            |
            |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+

             : : : : : : : : : : : : : : : :

            |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
   pitch6:  |s|         -1...478            |
            |-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+

\end{verbatim}
\caption{\label{bild9}Eine \textit{{FIFO}-Ebene im Detail}}

Die 5 Bit rechts oben sind dem \textit{Habitus} vorbehalten, also den maximal 32 \textit{Habitus}-Marken, die vordefinierte Registrierungen aus den Records abrufen und in Gang setzen.


\section{Wiederholungsschleifen}
Kurze Wiederholungen werden dem \textit{Generator} aufgebürdet, um den Pufferspeicher zu entlasten, da dieser ganze Partiturzeilen fassen muss. Zeichen für lange Wiederholungen wie in der üblichen Notenschrift gibt es hier nicht, denn ganze Partiturzeilen lassen sich per Editor bequem vervielfältigen.

Der Schleifenzähler (dessen Wert in der habit-Zelle mitgeschleppt wird) zählt die Anzahl der Wiederholungen (maximal 255) zurück bis Null. Die Schleifen sind 7-fach schachtelbar. Das erfordert einen \textit{Chevron}-Stack, und damit ist klar, dass innere Schleifen keine äußeren überdauern. Allerdings können bis zu 7 Schleifen zugleich beendet werden – dafür sorgt das \verb!>>>!-Feld (\textbf{Abb. \ref{bild9}}), das die Anzahl der zu schließenden Schleifen angibt.

Der Schleifenanfang wird durch  \texttt{cnt-lock} bestimmt; das Schleifenende sitzt irgendwo zwischen \texttt{cnt-lock} und \texttt{cnt-in}. Der Zähler \texttt{cnt-out} bezeichnet die Pufferzelle, aus der gerade gelesen wird. Bei  Wiederholungen wird er auf die Position von  \texttt{cnt-lock} zurückgesetzt.

Wenn bei \texttt{cnt-lock} ein älteres Schleifenende anzutreffen wäre, gäbe es ein Problem. Damit dies nicht geschieht, müssen schließende \textit{Chevrons} dem vorher notierten Ton mitgegeben werden. Deshalb werden „Töne“ aus dem \textit{Collector} erst mit einem folgenden Partiturzeichen – z. B. auch dem Zeilenende (\}), jedoch mit Ausnahme schließender \textit{Chevrons} (\verb!>  >>  >>>! etc.) – weitertransportiert.

\section{Der Loader und die gap-Marke}
Noch sind wir nicht beim \textit{Generator} angekommen, darum noch dies: Der \textit{Loader} übernimmt Daten aus dem \textit{FIFO}-Puffer und stellt sie dem \textit{Generator} zur Verfügung. Im \textit{Generator} läuft für die Dauer des aktuellen Tons ein Countdown-Zähler (Timer). Bei Null wird der nächste Ton angefordert.

Wegen der \textit{Staccato}-Funktion werden die Daten in einem Auffangregister (\textit{Latch}) zwischengespeichert, ehe sie zum \textit{Generator} gelangen. Im \textit{Latch} kann erkannt werden, ob ein nächster Ton oder eine Pause ansteht. Im Fall eines folgenden Tons wird der aktuelle Ton vorzeitig abgedämpft. Die gap-Marke des \textit{Timers} gibt die Größe des Vorlaufs an. Erreicht der \textit{Timer} eines der beteiligten Kanäle die gap-Marke, so werden neue Daten aus dem \textit{FIFO}-Puffer ins \textit{Latch} übertragen und das gap-Flag gesetzt. Folgt (im \textit{Latch}) eine Pause, so dauert der aktuelle Ton bis zum Zählerstand 0 an, worauf der Nachklang (\textit{Sustain}) einsetzt. Folgt (im \textit{Latch}) ein Ton, so werden bei Zählerstand 0 Daten vom \textit{Latch} in den \textit{Generator} übertragen. Das gap-Flag wird rückgesetzt.

Das gap-Flag ist erfordenlich, damit die \textit{FIFO}-Daten nur einmal (auch noch nach Unterschreiten der gap-Marke) ausgelesen werden. Am Anfang wird das gap-Flag gesetzt (weil gap-Marker unterschritten) und Daten aus dem \textit{FIFO} ins \textit{Latch} übertragen. Von dort gelangen sie sofort (weil Zählerstand Null) in den \textit{Generator} und der Zähler wird neu gesetzt. Mit Erreichen der gap-Marke gelangen nachfolgende Daten ins \textit{Latch}, etc.

Zunächst wird ein\textit{ okay-Flag} = true gesetzt und die beteiligten Kanäle aufgerufen. Wenn einer der Kanäle die gap-Marke erreicht und keinen Nachschub im \textit{FIFO}-Puffer vorfindet, wird das \textit{okay-Flag} = false gesetzt und dadurch der \textit{Generator} veranlasst, die Kontrolle an den Partitur-Scanner zurückzugeben.

\begin{verbatim}
: loader-A  ( f – f')
          requ-A_                \ request?
     IF   false to requ-A_       \ request off
          false to gap-A_        \ gap-Flag off
     THEN gap-A_ not             \ gap-Flag off?
IF        timer-A @ gaplim-A_ <= \ sentinel?
     IF   true to gap-A_         \ begin gap
          fifo>latch-A     and   \ update flag
THEN THEN timer-A @ 0=           \ timer=0?
     IF   false to gap-A_        \ end gap
          latch>gen-A      and   \ ld generator
     THEN ;

\end{verbatim}

\section{Symbole extrahieren}
Und ich sollte noch \texttt{extract-token} zeigen, das im Abschnitt Register-Syntax Verwendung fand:

\begin{verbatim}
\ Extract a symbol from a string
: extract-token  ( $a u tab-a -- $a' u' token f)
             >r _leading r>
    BEGIN    >r over r@ @ count    ( a u a $a $u)
             tuck str= not         ( a u f)
    WHILE    r> 2 cells +          ( a u a")
             dup @ 0=              ( a u a" f)
         IF  drop 0 0  EXIT THEN   ( a u 0 0)
    REPEAT   r@ @ count nip        ( a u $u)
             rot over + -rot -     ( a" u")
             r> cell+ true ;       ( a" u" a' t)

\ Beispiel für den Zugriff:

: do-A ." <a>" ;
: do-B ." <b>" ;
: do-C ." <c>" ;

: tabel$  ['] do-A  c" A#"
          ['] do-B  c" B#"
          ['] do-C  c" C#" ;

  create tabel tabel$  , ,  , ,  , ,  0 ,

: do-it  ( a u --)  tabel extract-token
          IF PERFORM  ELSE drop ." ???" THEN
          ."  | " type ;

\   s" A#"     do-it  =>  <a> |  ok
\   s" B#C#"   do-it  =>  <b> | C# ok
\   s" B# C# " do-it  =>  <b> | C#  ok
\   s" A#B#C#" do-it  =>  <a> | B#C# ok
\   s" X#B#C#" do-it  =>  ??? | X#B#C# ok

\end{verbatim}

Und damit genug für diesmal. Ich hoffe, dass das Gesamtkonzept ein bisschen deutlicher geworden ist. Der Umfang des \textit{Waver}-Programms lässt nicht zu, in alle Details (der funktionierenden Teile) zu gehen. Aber bislang hat die Beschreibung dem Projekt recht gut getan, denn es geht deutlich flotter voran. Das letzte Heft (2011/4) füllte sich schneller als erwartet, so dass ich leider zu spät kam. Das soll mir nicht wieder passieren.

\hspace*{\fill}

\begin{center}
Beachten Sie bitte den Dateibereich der Website der Forth-Gesellschaft unter\\
http://www.forth-ev.de/filemgmt/viewcat.php?cid=54\\
sowie die Site des Autors unter\\
 http://www.stocket.de/WE\\
\end{center}

\end{multicols}
\end{document}
