3

Problem description

I would basically like to recreate the following image in TikZ:

http://krashan.ppa.pl/articles/u1synth/adsr.png

This boils down to the question of how to multiply a sine wave with a piecewise linear function in TikZ/pgf.

Restrictions & freedoms

  • Colors from original image do not need to be reproduced :)
  • I'd like to solve this problem without having to store data in an external file
  • I'm happy to use pgfplots and/or pgfmath

Minimal (non-)working example

I am getting a feeling that the appropriate mechanism to attack this problem in TikZ/pgf is its function declaration mechanism (but I'm not sure whether I'm right).

In the example below, I have implemented only the first part of the piecewise linear function, to try and multiply it with a sine function. However, when I do so, the entire plot disappears (comment out the line marked TODO in the example below to confirm).

\documentclass{article}

\usepackage{pgfplots}

% Declare (first part of) piecewise linear function
\pgfmathdeclarefunction{p}{1}{%
  \pgfmathparse{ ((x>=0) && (x<=50))*x/50 }
}

\begin{document}

\begin{tikzpicture}[domain=0:500]

  \begin{axis}

    % Plot piecewise linear function
    \addplot[thick, blue]{p(x)};

    % Plot sine function
    \addplot[red, samples=500, smooth]{sin(20*x)};

    % Plot product of both. TODO: Makes entire plot disappear
    %\addplot[thick, green, samples=500, smooth]{sin(30*x)*p(x)};

  \end{axis}

\end{tikzpicture}

\end{document}

Bonus ideas for implementation

  • Signal max of piecewise linear function should always be +1
  • x axis should represent milliseconds
  • Total duration of envelope should be around 500ms, but:
  • It would be nice if the following parameters could be set by the user:
    • Total time T=A+D+S+R
    • Attack time A
    • Decay time D
    • Release time R
    • Sustain level L
  • The sustain time S would be automatically derived as S = T-(A+D+R)
  • Your question leaves all the effort to our community, even typing the essentials of a TeX document such as \documentclass{}...\begin{document} etc. As it is, most of our users will be very reluctant to touch your question, and you are left to the mercy of our procrastination team who are very few in number and very picky about selecting questions. You can improve your question by adding a minimal working example (MWE) that more users can copy/paste onto their systems to work on. If no hero takes the challenge we might have to close your question. – Stefan Pinnow Jan 25 '17 at 17:34
  • Yes, I am aware of that, but here my problem is precisely that I'm not sure which mechanism in TikZ/pgfplots is suitable to attack this problem in the first place. I'll try to add some more information, though. – Florian H. Jan 25 '17 at 17:38
  • 1
    I've added an MWE. – Florian H. Jan 25 '17 at 17:51
3

You can define a piecewise linear function by multiplying the pieces with the conditions that check for the interval and adding them. Then you just have to multiply the sine function with the piece-wise linear function.

What's the problem with your solution? In fact, there is only a single character missing: Add a percent sign after the \pgfmathparse expression:

\pgfmathdeclarefunction{p}{1}{%
  \pgfmathparse{ ((x>=0) && (x<=50))*x/50 }% <<<<<<<<
}

Without it, a space will be added to the left of the plot for every call of the function, so the plots moves out of the page towards the right.

enter image description here

\documentclass[border=5pt]{standalone}
\usepackage{pgfplots}
\usetikzlibrary{calc}

\pgfmathdeclarefunction{ADSR}{1}{%
  \pgfmathparse
    {(                  #1<=\pA      )*(#1/\pA)                          +%
     (and(#1>\pA      , #1<=(\pA+\pD))*(#1*(-\pL)/\pD + 1 + \pA*\pL/\pD) +%
     (and(#1>(\pA+\pD), #1<=(\pT-\pR))*(1-\pL)                           +%
     (and(#1>(\pT-\pR), #1<=\pT      )*((1-\pL)/\pR*(-#1+\pT))
    }%
}

\begin{document}
\begin{tikzpicture}
\newcommand\pT{500} % total
\newcommand\pA{100} % attack
\newcommand\pD{100} % decay
\newcommand\pR{100} % release
\newcommand\pL{0.2} % sustain level
\newcommand\pF{50}  % frequency (not in Hz, but proportional)
  \begin{axis}[x=0.2mm,y=2cm]
    \addplot[domain=0:\pT, green, samples=5000] {ADSR(x)*sin(\pF*x)};
    \addplot[domain=0:\pT, red, samples=100] {ADSR(x)};
  \end{axis}
\end{tikzpicture}
\end{document}
  • Great answer, thanks! Also for specifically pointing out the extra space at the EOL. – Florian H. Jan 26 '17 at 21:04
  • Quick comment after a closer look: The signal drops to 1-\pL = 0.8 in the sustain phase, whereas my idea was to have it drop to \pL = 0.2. It should be easy to fix that by replacing all occurences of \pL with (1-\pL) and vice versa, though. – Florian H. Jan 26 '17 at 21:20
  • @FlorianH. Well, the image that you link to defines "sustain level" the way I have implemented it. – gernot Jan 27 '17 at 20:30
  • Ah, good point - I had not paid attention to that. Yes, there are different definitions of "sustain level", so I suggest for the sake of clarity with regards to the original image, we leave the answer as it is - it is clear enough, really. Thanks! – Florian H. Jan 29 '17 at 15:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.