AnalogTapeModel

Physical modelling signal processing for analog tape recording
Log | Files | Refs | Submodules | README | LICENSE

commit 9c6fe6d76243606cdc88a4967c124474830debb6
parent e64ee92001449b9595d6847405aaa2f2621a18b5
Author: jatinchowdhury18 <jatinchowdhury18@users.noreply.github.com>
Date:   Mon, 25 Feb 2019 16:49:35 -0800

[skip travis] update paper

Diffstat:
MPaper/420_paper.pdf | 0
MPaper/420_paper.tex | 145++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
APaper/tikz-dsp.sty | 22++++++++++++++++++++++
APaper/tikzlibrarydsp.code.tex | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 386 insertions(+), 19 deletions(-)

diff --git a/Paper/420_paper.pdf b/Paper/420_paper.pdf Binary files differ. diff --git a/Paper/420_paper.tex b/Paper/420_paper.tex @@ -91,6 +91,11 @@ \usepackage[figure,table]{hypcap} \fi \usepackage{cleveref} +\usepackage{tikz} +\usetikzlibrary{dsp,chains} + +\DeclareMathAlphabet{\mathpzc}{OT1}{pzc}{m}{it} +\newcommand{\z}{\mathpzc{z}} \title{\papertitle} @@ -138,11 +143,11 @@ the tape ($y$). Using the Karlqvist medium field approximation, we find \begin{multline} H_x(x,y) = \frac{1}{\pi} H_0 \Big(\tan^{-1} \Big(\frac{(g/2) + x}{y} \Big) \\ + \tan^{-1} \Big(\frac{(g/2) - x}{y} \Big) \Big) - \label{eq1} + \label{eq:H_x} \end{multline} \begin{equation} H_y(x,y) = \frac{1}{2 \pi} H_0 \ln \Big(\frac{((g/2) - x)^2 + y^2}{((g/2) + x)^2 + y^2} \Big) - \label{eq2} + \label{eq:H_y} \end{equation} % where $g$ is the head gap, and $H_0$ is the deep gap field, given by: @@ -219,7 +224,7 @@ The ideal playback voltage as a function of tape magnetisation is given by \begin{equation} V(x) = NWEv \mu_0 \int_{-\infty}^{\infty} dx' \int_{-\delta/2}^{\delta/2} dy' \vec{h}(x' + x, y') \cdot \frac{\vec{M}(x', y')}{dx} - \label{eq11} + \label{eq:Vx} \end{equation} % where $N$ is the number of turns of wire, $W$ is the width of the playhead, $E$ is the playhead @@ -231,7 +236,7 @@ Note that $V(x) = V(vt)$ for constant $v$. $\vec{h}(x, y)$ is defined as: \label{eq12} \end{equation} % -where $\vec{H} (x, y)$ can be calculated by \cref{eq1,eq2}. +where $\vec{H} (x, y)$ can be calculated by \cref{eq:H_x,eq:H_y}. \subsubsection{Loss Effects} There are several frequency-dependent loss effects associated with playback, @@ -258,6 +263,7 @@ where $f$ is the frequency and $v$ is the tape speed. For simplicity, let us assume, \begin{equation} \vec{H}(x,y,t) = \vec{H}(0,0,t) + \label{eq:spatialApprox} \end{equation} % In this case $H_y \equiv 0$, and $H_x \equiv H_0$. Thus, @@ -312,6 +318,7 @@ for $\hat{M}(n)$: k_3 &= T f \Big(n- \frac{1}{2}, \hat{M}(n-1) + \frac{k_2}{2}, \hat{\vec{u}} \Big(n-\frac{1}{2} \Big) \Big)\\ k_4 &= T f \Big(n, \hat{M}(n-1) + k_3, \hat{\vec{u}}(n) \Big)\\ \hat{M}(n) &= \hat{M}(n-1) + \frac{k_1}{6} + \frac{k_2}{3} + \frac{k_3}{3} + \frac{k_4}{6} + \label{eq:Mn} \end{split} \end{align} % @@ -347,7 +354,7 @@ meter, the following plot shows the Magnetisation output. \end{figure} % \subsection{Play Head} -By combining \cref{eq11} with \cref{eq12,eq15}, we get: +By combining \cref{eq:Vx} with \cref{eq12,eq15}, we get: \begin{equation} V(t) = NWEv \mu_0 g M(t) \end{equation} @@ -486,7 +493,7 @@ the pitch of the output signal. To characterize these timing imperfections, we use a method similar to \cite{tapeDelay}: We recorded a pulse train of 1000 pulses through a TC-260, then recorded the pulses back from the tape. \Cref{timingSim} -below shows a section of a superimposed plot of the original +shows a section of a superimposed plot of the original pulse train against the pulse train recorded from the tape machine. From this data, we were able to generate a periodic function that accurately models the timing imperfections of @@ -503,22 +510,122 @@ signature "wow" effect of an analog tape machine. % \section{Real-Time Implementation} -@TODO -\subsection{Oversampling} %@TODO; move this section somewhere else +We implemented our physical model of the Sony TC-260 as a +VST audio plugin using the JUCE framework. \Cref{flowchart} +shows the signal flow of the system in detail. We allow the user +to control ``normal'' parameters including the tape speed, +oversampling factor, bias frequency and bias gain. +Other ``unrealistic'' parameters were added, including +real-time control of gap width, tape thickness, tape spacing, +and flutter depth. C/C++ code for the real-time implementation +is available on GitHub +\footnote{\url{https://github.com/jatinchowdhury18/AnalogTapeModel}}. + +\begin{figure*}[ht] + \center + \begin{tikzpicture} + \matrix (m1) [row sep=10mm, column sep=7.5mm] + { + & + & + \node[dspnodeopen,dsp/label=above] (b0) {Bias Signal}; & + & + & + & + & + \node[dspnodeopen,dsp/label=above] (b1) {Flutter ($\tau$)}; & + \node[dspnodeopen,dsp/label=above] (b2) {Tape Speed ($v$)}; \\ + %--------------------------------------------------------------- + \node[dspnodeopen,dsp/label=left] (x0) {$x[n]$}; & + \node[dspsquare] (x1) {\upsamplertext{M}}; & + \node[dspadder] (x2) {}; & + \node[dspfilter] (x3) {$H_{record}$}; & + \node[dspfilter] (x4) {Hysteresis}; & + \node[dspfilter] (x5) {De-bias}; & + \node[dspsquare] (x6) {\downsamplertext{M}}; & + \node[dspsquare] (x7) {$z^{-\tau}$}; & + \node[dspfilter] (x8) {$H_{play}(v)$}; & + \node[dspnodeopen,dsp/label=right] (x9) {$y[n]$}; \\ + }; + \draw[dspconn] (b0) -- (x2); + \draw[dspconn] (b1) -- (x7); + \draw[dspconn] (b2) -- (x8); + \foreach \i [evaluate = \i as \j using int(\i+1)] in {0,1,...,8} + \draw[dspconn] (x\i) -- (x\j); + \end{tikzpicture} + \caption{\label{flowchart}{\it Flowchart of realtime system: + M is the oversampling factor, + $H_{record}$ + is the transfer function of the record + head, and + $H_{play}$ is the play head transfer + function including loss effects, and a + de-biasing filter.}} +\end{figure*} +% +\subsection{Oversampling} If no oversampling is used, the system will be unstable for input signal at the Nyquist frequency, due to limitations of the trapezoid rule derivate approximation used in \cref{eq:hDeriv}. -To avoid this, a lowpass filter with cutoff frequency below Nyquist -should suffice. However, due to aliasing caused by the nonlinearity -of the tape hysteresis model, oversampling is necessary to mitigate -aliasing artifacts \cite{Yeh}. Further, the system must be able to -faithfully recreate not only the frequencies in the audible range -but the bias frequencies as well. Since the minimum standard audio sampling -rate is 44.1 kHz, and the minimum standard biasing frequency is approximately -50 kHz \cite{Camras:1987:MRH:27189}, a minimum oversampling factor of 3x is -required. In the real-time implementation, we use a minimum -oversampling factor of 4x, with options to oversample at 8x or 16x -if the user desires. +To avoid this instability, early versions of the real-time +implementation used a lowpass filter with cutoff frequency +just below Nyquist. However, due to aliasing caused by the +nonlinearity of the tape hysteresis model, oversampling is +necessary to mitigate aliasing artifacts \cite{Yeh}. Further, +the system must be able to faithfully recreate not only the +frequencies in the audible range but the bias frequencies as +well. Since the minimum standard audio sampling rate is 44.1 +kHz, and the minimum standard biasing frequency is approximately +50 kHz \cite{Camras:1987:MRH:27189}, a minimum oversampling +factor of 3x is required. In the real-time implementation, +we use a minimum oversampling factor of 4x, with options to +oversample at 8x or 16x if the user desires. + +\subsection{Performance} +The performance of the real time system is heavily +dependent on the oversampling factor. At an oversampling +factor of 2x or 4x, the system operates with a reasonable +CPU cost. At higher oversampling rates, the CPU cost quickly +becomes unreasonably large compared to a typical VST plugin. + +\subsection{Results} +In subjective testing, our physical model sounds quite convincing, +with warm, tape-like distortion, and realistic sounding +flutter. The high-frequency loss and low-frequency +``head bump'' change correctly at different tape speeds, +and are approximately within the frequency response +specifications of the TC-260 service manual \cite{RefManual}. +The distortion and frequency response characteristics +of our model are subjectivelyvery close when compared to +the output of an actual TC-260. +The largest difference between the model and the physical +machine is the subtle electrical and mechanical noises +and dropouts present in the physical machine, presumably +caused by the age and wear-and-tear of the machine, which +we did not attempt to characterize in our model. +\newline\newline +% TODO: Generate plots and sound examples from real-time system + +\section{Future Improvements} +\subsection{Spatial Magnetic Effects} +The most obvious improvement to be made for the physical model +is the inclusion of spatial effects of the tape. In particular, +the approximation made in \cref{eq:spatialApprox}, negate any +effects caused by magnetisation along the longitudinal length +of the tape, and into the depth of the tape. Including spatial +effects would involve deriving digital analogues for +\cref{eq:H_x,eq:H_y,eq:Vx}, and re-deriving \cref{eq:Mn} +to take an 2-dimensional magnetic field input per timestep, +rather than the zero-dimensional input. This change +would greatly increase the computational complexity of the system, +since solving \cref{eq:Mn} currently dominates the computational +complexity part of the system. Using an oversampling rate of 4x, +with just 100 spatial samples, would be 400x more +computationally complex than the current system. + +\section{Acknowledgements} +Many thanks to Julius Smith for guidance and support, and +thanks to Irene Abosch for kindly donating her Sony TC-260. %\newpage \nocite{*} diff --git a/Paper/tikz-dsp.sty b/Paper/tikz-dsp.sty @@ -0,0 +1,22 @@ +% ------------------------------------------------------------------------- +% This library for block diagrams and signal flow graphs was inspired by +% the library "signalflow" of Dr. Karlheinz Ochs, Ruhr-University of Bochum, +% Germany. Furthermore, some ideas were taken from the library "circuitikz" +% of Massimo A. Redaelli and from the PGF library itself. +% +% Copyright 2012 by Matthias Hotz +% +% This work is licensed under the Creative Commons Attribution 2.5 Generic +% License. To view a copy of this license, visit +% http://creativecommons.org/licenses/by/2.5/ +% or send a letter to Creative Commons, 444 Castro Street, Suite 900, +% Mountain View, California, 94041, USA. +% ------------------------------------------------------------------------- + +\NeedsTeXFormat{LaTeX2e} +\RequirePackage{tikz} + +\def\tikzdspversion{0.1} +\ProvidesPackage{tikz-dsp}[2012/09/07 The digital signal processing drawing package \tikzdspversion] + +\input{tikzlibrarydsp.code.tex} diff --git a/Paper/tikzlibrarydsp.code.tex b/Paper/tikzlibrarydsp.code.tex @@ -0,0 +1,238 @@ +% ------------------------------------------------------------------------- +% This library for block diagrams and signal flow graphs was inspired by +% the library "signalflow" of Dr. Karlheinz Ochs, Ruhr-University of Bochum, +% Germany. Furthermore, some ideas were taken from the library "circuitikz" +% of Massimo A. Redaelli and from the PGF library itself. +% +% Copyright 2012 by Matthias Hotz +% +% This work is licensed under the Creative Commons Attribution 2.5 Generic +% License. To view a copy of this license, visit +% http://creativecommons.org/licenses/by/2.5/ +% or send a letter to Creative Commons, 444 Castro Street, Suite 900, +% Mountain View, California, 94041, USA. +% ------------------------------------------------------------------------- + +\usetikzlibrary{arrows, calc, positioning, decorations.markings} + +% ------------------------------------------------------------------------- +% Parameters for the library + +\newcommand{\dsplinewidth}{0.25mm} % Line width for connections +\newcommand{\dspblocklinewidth}{0.3mm} % Line width for blocks +\newcommand{\dspoperatordiameter}{4mm} % Diameter for adder, multiplier, mixer +\newcommand{\dspoperatorlabelspacing}{2mm} % Distance from symbol to label for adder, multiplier, mixer +\newcommand{\dspnoderadius}{1mm} % Filled and empty node +\newcommand{\dspsquareblocksize}{8mm} % Size for square blocks, e.g. for delay elements, decimator, expander +\newcommand{\dspfilterwidth}{14mm} % Width of a filter block + +% ------------------------------------------------------------------------- +% Define new arrow heads + +\pgfarrowsdeclare{dsparrow}{dsparrow} +{ + \arrowsize=0.25pt + \advance\arrowsize by .5\pgflinewidth + \pgfarrowsleftextend{-4\arrowsize} + \pgfarrowsrightextend{4\arrowsize} +} +{ + \arrowsize=0.25pt + \advance\arrowsize by .5\pgflinewidth + \pgfsetdash{}{0pt} % Solid line (do not dash) + \pgfsetmiterjoin % Fixed miter join of line + \pgfsetbuttcap % Fixed butt cap of line + \pgfpathmoveto{\pgfpoint{-4\arrowsize}{2.5\arrowsize}} + \pgfpathlineto{\pgfpoint{4\arrowsize}{0pt}} + \pgfpathlineto{\pgfpoint{-4\arrowsize}{-2.5\arrowsize}} + \pgfpathclose + \pgfusepathqfill +} + +\pgfarrowsdeclare{dsparrowmid}{dsparrowmid} +{ + \arrowsize=0.25pt + \advance\arrowsize by .5\pgflinewidth + \pgfarrowsleftextend{-4\arrowsize} + \pgfarrowsrightextend{4\arrowsize} +} +{ + \arrowsize=0.25pt + \advance\arrowsize by .5\pgflinewidth + \pgfsetdash{}{0pt} + \pgfsetmiterjoin + \pgfsetbuttcap + \pgfpathmoveto{\pgfpoint{0}{2.5\arrowsize}} + \pgfpathlineto{\pgfpoint{8\arrowsize}{0pt}} + \pgfpathlineto{\pgfpoint{0}{-2.5\arrowsize}} + \pgfpathclose + \pgfusepathqfill +} + +% ------------------------------------------------------------------------- +% Define new node shapes + +\makeatletter + +\pgfkeys{/tikz/dsp/label/.initial=above} + +% Generic shape generator for operators, i.e. nodes with a circular +% shape with an additional customizable drawing and a text label +\long\def\dspdeclareoperator#1#2{ + \pgfdeclareshape{#1} + { + % Saved anchors, macros and dimensions + \savedanchor\centerpoint{\pgfpointorigin} + \savedmacro\label{\def\label{\pgfkeysvalueof{/tikz/dsp/label}}} + \saveddimen\radius + { + \pgfmathsetlength\pgf@xa{\pgfshapeminwidth} + \pgfmathsetlength\pgf@ya{\pgfshapeminheight} + \ifdim\pgf@xa>\pgf@ya + \pgf@x=.5\pgf@xa + \else + \pgf@x=.5\pgf@ya + \fi + } + + % Inherit all anchors from the 'circle'-shape: + \inheritanchor[from={circle}]{center} + \inheritanchor[from={circle}]{mid} + \inheritanchor[from={circle}]{base} + \inheritanchor[from={circle}]{north} + \inheritanchor[from={circle}]{south} + \inheritanchor[from={circle}]{west} + \inheritanchor[from={circle}]{east} + \inheritanchor[from={circle}]{mid west} + \inheritanchor[from={circle}]{mid east} + \inheritanchor[from={circle}]{base west} + \inheritanchor[from={circle}]{base east} + \inheritanchor[from={circle}]{north west} + \inheritanchor[from={circle}]{south west} + \inheritanchor[from={circle}]{north east} + \inheritanchor[from={circle}]{south east} + \inheritanchorborder[from={circle}] + + % Draw circle and embed additional code + \backgroundpath + { + % Draw circle + \pgfpathcircle{\centerpoint}{\radius} + + % Embed additional code + % (Note that this code must call e.g. \pgfusepathqstroke + #2 + } + + % Define anchor parametrized by the PGF key /tikz/dsp/label + \anchor{text} + { + \centerpoint + % + \def\templabelabove{above} + \def\templabelbelow{below} + \def\templabelleft{left} + \def\templabelright{right} + \pgfutil@tempdima=\dspoperatorlabelspacing + % + \ifx\label\templabelabove + \advance\pgf@x by -0.5\wd\pgfnodeparttextbox + \advance\pgf@y by \radius + \advance\pgf@y by \pgfutil@tempdima + \fi + % + \ifx\label\templabelbelow + \advance\pgf@x by -0.5\wd\pgfnodeparttextbox + \advance\pgf@y by -\radius + \advance\pgf@y by -\pgfutil@tempdima + \advance\pgf@y by -\ht\pgfnodeparttextbox + \fi + % + \ifx\label\templabelleft + \advance\pgf@x by -\radius + \advance\pgf@x by -\pgfutil@tempdima + \advance\pgf@x by -\wd\pgfnodeparttextbox + \advance\pgf@y by -0.5\ht\pgfnodeparttextbox + \advance\pgf@y by +0.5\dp\pgfnodeparttextbox + \fi + % + \ifx\label\templabelright + \advance\pgf@x by \radius + \advance\pgf@x by \pgfutil@tempdima + \advance\pgf@y by -0.5\ht\pgfnodeparttextbox + \advance\pgf@y by +0.5\dp\pgfnodeparttextbox + \fi + } + } +} + +\dspdeclareoperator{dspshapecircle}{\pgfusepathqstroke} + +\dspdeclareoperator{dspshapecirclefull}{\pgfusepathqfillstroke} + +\dspdeclareoperator{dspshapeadder}{ + % Coordinate offset for the plus + \pgfutil@tempdima=\radius + \pgfutil@tempdima=0.55\pgfutil@tempdima + + % Draw plus + \pgfmoveto{\pgfpointadd{\centerpoint}{\pgfpoint{0pt}{-\pgfutil@tempdima}}} + \pgflineto{\pgfpointadd{\centerpoint}{\pgfpoint{0pt}{ \pgfutil@tempdima}}} + + \pgfmoveto{\pgfpointadd{\centerpoint}{\pgfpoint{-\pgfutil@tempdima}{0pt}}} + \pgflineto{\pgfpointadd{\centerpoint}{\pgfpoint{ \pgfutil@tempdima}{0pt}}} + + \pgfusepathqstroke +} + +\dspdeclareoperator{dspshapemixer}{ + % Coordinate offset for the cross + \pgfutil@tempdima=\radius + \pgfutil@tempdima=0.707106781\pgfutil@tempdima + + % Draw cross + \pgfmoveto{\pgfpointadd{\centerpoint}{\pgfpoint{-\pgfutil@tempdima}{-\pgfutil@tempdima}}} + \pgflineto{\pgfpointadd{\centerpoint}{\pgfpoint{ \pgfutil@tempdima}{ \pgfutil@tempdima}}} + + \pgfmoveto{\pgfpointadd{\centerpoint}{\pgfpoint{-\pgfutil@tempdima}{ \pgfutil@tempdima}}} + \pgflineto{\pgfpointadd{\centerpoint}{\pgfpoint{ \pgfutil@tempdima}{-\pgfutil@tempdima}}} + + \pgfusepathqstroke +} + +\makeatother + +% ------------------------------------------------------------------------- +% Define node styles + +\tikzset{dspadder/.style={shape=dspshapeadder,line cap=rect,line join=rect, + line width=\dspblocklinewidth,minimum size=\dspoperatordiameter}} +\tikzset{dspmultiplier/.style={shape=dspshapecircle,line cap=rect,line join=rect, + line width=\dspblocklinewidth,minimum size=\dspoperatordiameter}} +\tikzset{dspmixer/.style={shape=dspshapemixer,line cap=rect,line join=rect, + line width=\dspblocklinewidth,minimum size=\dspoperatordiameter}} + +\tikzset{dspnodeopen/.style={shape=dspshapecircle,line width=\dsplinewidth,minimum size=\dspnoderadius}} +\tikzset{dspnodefull/.style={shape=dspshapecirclefull,line width=\dsplinewidth,fill,minimum size=\dspnoderadius}} + +% The fixed specification of text height and text depth is the somewhat +% unaesthetic workaround to align the text in different node at the same +% baseline. See the PGF/TikZ manual, ch. 5.1. +\tikzset{dspsquare/.style={shape=rectangle,draw,align=center,text depth=0.3em,text height=1em,inner sep=0pt, + line cap=round,line join=round,line width=\dsplinewidth,minimum size=\dspsquareblocksize}} +\tikzset{dspfilter/.style={shape=rectangle,draw,align=center,text depth=0.3em,text height=1em,inner sep=0pt, + line cap=round,line join=round,line width=\dsplinewidth,minimum height=\dspsquareblocksize,minimum width=\dspfilterwidth}} + +% ------------------------------------------------------------------------- +% Define "signal flow" lines + +\tikzset{dspline/.style={line width=\dsplinewidth},line cap=round,line join=round} +\tikzset{dspconn/.style={->,>=dsparrow,line width=\dsplinewidth},line cap=round,line join=round}%line cap=rect,line join=miter} +\tikzset{dspflow/.style={line width=\dsplinewidth,line cap=round,line join=round, + decoration={markings,mark=at position 0.5 with {\arrow{dsparrowmid}}},postaction={decorate}}} + +% ------------------------------------------------------------------------- +% Define various utility macros + +\newcommand{\downsamplertext}[1]{\raisebox{0.1em}{$\big\downarrow$}#1} +\newcommand{\upsamplertext}[1]{\raisebox{0.1em}{$\big\uparrow$}#1}