ForSyDe-3.0: examples/Equalizer_Shallow/ButtonControl.lhs
\subsection{Overview}
The subsystem \process{Button Control} works as a user interface in the equalizer system. It receives the four input signals \signal{BassDn}, \signal{BassUp}, \signal{TrebleDn}, \signal{TrebleUp} and the override signal \process{Override} from the \process{Distortion Control} and calculates the new bass and treble values for the output signals \signal{Bass} and \signal{Treble}. The subsytem contains the main processes \process{Button Interface} and \process{Level Control}. The process \process{Level Control} outputs a new value, if either the signal \signal{Button} or the signal \signal{Overr} is present, otherwise the output value is absent. The process \process{Hold Level} is modeled by means of \process{holdSY (0.0, 0.0)} that outputs the last present value, if the input value is absent. The process \process{unzipSY} transforms a signal of tuples (the current bass and treble level) into a tuple of signals (a bass and a treble signal).
\begin{figure}
\centering
\scalebox{0.8}{\mbox{\input{Figures/ButtonControl.pstex_t}}}
\caption{The Subsystem \process{Button Control}}
\end{figure}
\begin{code}
module ButtonControl (buttonControl) where
import ForSyDe.Shallow
import EqualizerTypes
import Combinators
data State = Operating
| Locked deriving(Eq, Show)
type Level = Double
type Bass = Level
type Treble = Level
buttonControl :: Signal (AbstExt OverrideMsg) -> Signal (AbstExt Sensor)
-> Signal (AbstExt Sensor) -> Signal (AbstExt Sensor)
-> Signal (AbstExt Sensor) -> (Signal Bass,Signal Treble)
buttonControl overrides bassDn bassUp trebleDn trebleUp
= (bass, treble)
where (bass, treble) = unzipSY levels
levels = ((holdSY (0.0, 0.0)) `funComb2` levelControl)
button overrides
button = buttonInterface bassDn bassUp trebleDn trebleUp
\end{code}
\subsection{The Process \process{Button Interface}}
The \process{Button Interface} monitors the four input buttons \signal{BassDn}, \signal{BassUp}, \signal{TrebleDn}, \signal{TrebleUp} and indicates if a button is pressed. If two or more buttons are pressed the conflict is resolved by the priority order of the buttons.
\begin{code}
buttonInterface :: Signal (AbstExt Sensor) -> Signal (AbstExt Sensor)
-> Signal (AbstExt Sensor) -> Signal (AbstExt Sensor)
-> Signal (AbstExt Button)
buttonInterface bassUp bassDn trebleUp trebleDn
= zipWith4SY f bassUp bassDn trebleUp trebleDn
where f (Prst Active) _ _ _ = Prst BassUp
f _ (Prst Active) _ _ = Prst BassDn
f _ _ (Prst Active) _ = Prst TrebleUp
f _ _ _ (Prst Active) = Prst TrebleDn
f _ _ _ _ = Abst
\end{code}
\subsection{The Process \process{Level Control}}
The process has a local state that consists of a mode and the current values for the bass and treble levels (Figure \ref{fig:FSM_LevelControl}). The \process{Level Control} has two modes, in the mode \constant{Operating} the bass and treble values are stepwise changed in 0.2 steps. However, there exists maximum and minimum values which are -5.0 and +5.0. The process enters the mode \constant{Locked} when the \constant{Override} input has the value \constant{Lock}. In this mode an additional increase of the bass level is prohibitet and even decreased by 1.0 in case the \constant{Override} signal has the value \constant{CutBass}. The subsystem returns to the \constant{Operating} mode on the override value \constant{Release}. The output of the process is an absent extended signal of tuples with the current bass and treble levels.
\begin{figure}
\resizebox{\columnwidth}{!}{\mbox{\input{Figures/FSM_LevelControl.pstex_t}}}
\caption{The State Diagram of the Process \process{Level Control}}
\label{fig:FSM_LevelControl}
\end{figure}
\begin{code}
levelControl :: Signal (AbstExt Button) -> Signal (AbstExt OverrideMsg)
-> Signal (AbstExt (Bass,Treble))
levelControl button overrides
= mealy2SY nextState output (initState, initLevel) button overrides
nextState :: (State,(Double,Double)) -> AbstExt Button
-> AbstExt OverrideMsg -> (State,(Double,Double))
nextState (state, (bass, treble)) button override
= (newState, (newBass, newTreble)) where
newState = if state == Operating then
if override == Prst Lock then
Locked
else
Operating
else
if override == Prst Release then
Operating
else
Locked
newBass = if state == Locked then
if override == Prst CutBass then
decreaseLevel bass cutStep
else
if button == Prst BassDn then
decreaseLevel bass step
else
bass
else -- state = Operating
if button == Prst BassDn then
decreaseLevel bass step
else
if button == Prst BassUp then
increaseLevel bass step
else
bass
newTreble = if button == Prst TrebleDn then
decreaseLevel treble step
else
if button == Prst TrebleUp then
increaseLevel treble step
else
treble
output :: (a, (Bass, Treble)) -> AbstExt Button -> AbstExt OverrideMsg
-> AbstExt (Bass,Treble)
output _ Abst Abst = Abst
output (_, levels) _ _ = Prst levels
\end{code}
The process uses the following initial values.
\begin{code}
initState = Operating
initLevel = (0.0, 0.0)
maxLevel = 5.0
minLevel = -5.0
step = 0.2
cutStep = 1.0
\end{code}
The process uses the following auxiliary functions.
\begin{code}
decreaseLevel :: Level -> Level -> Level
decreaseLevel level step = if reducedLevel >= minLevel then
reducedLevel
else
minLevel
where reducedLevel = level - step
increaseLevel :: Level -> Level -> Level
increaseLevel level step = if increasedLevel <= maxLevel then
increasedLevel
else
maxLevel
where increasedLevel = level + step
\end{code}