From feb2e558242cf7b9293780c75dc4d4594f41ecc9 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 25 Jun 2018 05:41:45 -0400 Subject: [PATCH] Migrate from GitBook to Sphinx --- .gitignore | 3 +- .gitmodules | 0 Bridge.md | 8 +- Communities.md | 20 +++ Core.md | 16 +-- DSP.md | 146 ++++++++++----------- Installing.md | 2 +- Introduction.md | 26 ---- Makefile | 19 ++- README.md | 16 +-- TOC.md | 24 ---- Toolbar.md | 2 +- VoltageStandards.md | 2 +- _static/overrides.css | 13 ++ book.json | 32 ----- conf.py | 99 ++++++++++++++ images/{Bridge Live.png => BridgeLive.png} | Bin images/logo.png | Bin 2070 -> 1165 bytes index.rst | 45 +++++++ 19 files changed, 278 insertions(+), 195 deletions(-) create mode 100644 .gitmodules create mode 100644 Communities.md delete mode 100644 Introduction.md delete mode 100644 TOC.md create mode 100644 _static/overrides.css delete mode 100644 book.json create mode 100644 conf.py rename images/{Bridge Live.png => BridgeLive.png} (100%) create mode 100644 index.rst diff --git a/.gitignore b/.gitignore index f0a5279..5cff9a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -node_modules -/_book \ No newline at end of file +/_build \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/Bridge.md b/Bridge.md index 687e214..8c5780d 100644 --- a/Bridge.md +++ b/Bridge.md @@ -1,4 +1,4 @@ -# VCV Bridge +# Bridge Rack is a standalone DAW-like application and not a VST/AU plugin because of the major limitations of these formats. It is common to think of physical modular synthesizers as entire self-contained DAWs, so many people use Rack as a complete DAW to compose music and build patches without other software. @@ -9,12 +9,12 @@ The setup order between Rack and your DAW does not matter. ### Setting up Bridge in Rack -- Add an Audio or MIDI module to Rack from the [Core](Core.md) plugin, and select "Bridge" from the driver dropdown list. +- Add an Audio or MIDI module to Rack from the [Core](Core.html) plugin, and select "Bridge" from the driver dropdown list. - Open the device menu to select the Bridge port. Up to 8 channels of audio entering the Bridge effect plugin are routed to the INPUT section of the Audio module in Rack and then back to the effect plugin. -The 16 automation parameters in the VST/AU Bridge plugin simply generate MIDI-CC messages 0-15, so you can use a [Core MIDI-CC](Core.md#midi-cc) interface to convert them to 0-10 V signals in Rack. +The 16 automation parameters in the VST/AU Bridge plugin simply generate MIDI-CC messages 0-15, so you can use a [Core MIDI-CC](Core.html#midi-cc) interface to convert them to 0-10 V signals in Rack. ### Setting up Bridge in your DAW @@ -32,7 +32,7 @@ To send audio to Rack, select the Bridge's track under the "Audio To" menu on an To record audio from Rack, create a new audio track and select the Bridge's track and optionally the channel pair under the "Audio From" menu. Make sure "Monitor" is set to "In" on the Bridge's track to enable audio output even when it is not record-enabled. -![Ableton Live VCV Bridge](images/Bridge Live.png) +![Ableton Live VCV Bridge](images/BridgeLive.png) #### Cubase TODO diff --git a/Communities.md b/Communities.md new file mode 100644 index 0000000..0106afd --- /dev/null +++ b/Communities.md @@ -0,0 +1,20 @@ + +### Communities + +- [VCVRack.com](https://vcvrack.com/) +- [Facebook page](https://www.facebook.com/vcvrack/) +- [Twitter](https://twitter.com/vcvrack) +- [Github issue tracker (features, bugs, and developer discussions)](https://github.com/VCVRack/Rack/issues) +- [Facebook user group](https://www.facebook.com/groups/vcvrack/) +- [Facebook developer group](https://www.facebook.com/groups/2035785263299933/) +- [Facebook French user group](https://www.facebook.com/groups/2069785583250645/) +- [Switched On Rack blog](http://www.switchedonrack.com/) +- [MuffWiggler thread](https://www.muffwiggler.com/forum/viewtopic.php?t=186899) +- [KVR Audio thread](https://www.kvraudio.com/forum/viewtopic.php?f=23&t=489230) +- [Hispasonic thread (Español)](https://www.hispasonic.com/foros/foro-vcv-rack/516252) +- [Patchstorage](https://patchstorage.com/platform/vcv-rack/) +- [Discord](https://discord.gg/wxa89Mh) +- [Reddit](https://www.reddit.com/r/vcvrack/) +- [Twitch](https://www.twitch.tv/communities/vcvrackcommunity) +- [Slack developers chat](https://vcvrackdevelopers.slack.com/join/shared_invite/enQtMzczNzY2NDUzMTczLWM2Mjg0ZjEzNDQ2YTEwYjFiZTA3MzE4NTRjMjg5ZjRkZDAwMDk5M2I4NmIzYmEyZGY1NGQ4YWE4NzkzZjlhMmI) +- [IRC](http://webchat.freenode.net?channels=%23VCVRack) diff --git a/Core.md b/Core.md index 99e5379..9ad2aaf 100644 --- a/Core.md +++ b/Core.md @@ -4,14 +4,14 @@ The *Core* plugin (built into Rack itself) includes utilities and interfaces for #### Modules -- [Audio](Core.md#audio) -- [MIDI Interfaces](Core.md#midi-interfaces) - - [MIDI-1](Core.md#midi-1) - - [MIDI-4](Core.md#midi-4) - - [MIDI-CC](Core.md#midi-cc) - - [MIDI-Trig](Core.md#midi-trig) -- [Blank](Core.md#blank) -- [Notes](Core.md#notes) +- [Audio](#audio) +- [MIDI Interfaces](#midi-interfaces) + - [MIDI-1](#midi-1) + - [MIDI-4](#midi-4) + - [MIDI-CC](#midi-cc) + - [MIDI-Trig](#midi-trig) +- [Blank](#blank) +- [Notes](#notes) ### Audio diff --git a/DSP.md b/DSP.md index 44c90a9..e1bb1a1 100644 --- a/DSP.md +++ b/DSP.md @@ -27,7 +27,7 @@ Image credits are from Wikipedia. ### Signals -A *signal* is a function $x(t): \mathbb{R} \rightarrow \mathbb{R}$ of amplitudes (voltages, sound pressure levels, etc.) defined on a time continuum, and a *sequence* is a function $x(n): \mathbb{Z} \rightarrow \mathbb{R}$ defined only at integer points, often written as $x_n$. +A *signal* is a function \\(x(t): \mathbb{R} \rightarrow \mathbb{R}\\) of amplitudes (voltages, sound pressure levels, etc.) defined on a time continuum, and a *sequence* is a function \\(x(n): \mathbb{Z} \rightarrow \mathbb{R}\\) defined only at integer points, often written as \\(x_n\\). In other words, a signal is an infinitely long continuum of values with infinite time detail, and a sequence is an infinitely long stream of samples at evenly-spaced isolated points in time. Analog hardware produces and processes signals, while digital algorithms handle sequences of samples (often called *digital signals*.) @@ -35,31 +35,31 @@ Analog hardware produces and processes signals, while digital algorithms handle ### Fourier analysis -In DSP you often encounter periodic signals that repeat at a frequency $f$, like cyclic waveforms. -You may be aware that periodic signals are composed of multiple harmonics (shifted cosine waves) with frequencies $fk$ for $k = 1, 2, 3, \ldots$. +In DSP you often encounter periodic signals that repeat at a frequency \\(f\\), like cyclic waveforms. +You may be aware that periodic signals are composed of multiple harmonics (shifted cosine waves) with frequencies \\(fk\\) for \\(k = 1, 2, 3, \ldots\\). In fact, a periodic signal can be *defined* by the amplitudes and phases of all of its harmonics. -For simplicity, if $x(t)$ is a periodic signal with $f=1$, then -$$ x(t) = A_0 + \sum_{k=1}^\infty A_k \cos \left( 2\pi k t + \phi_k \right) $$ -where $A_0$ is the *DC component* (the signal's average offset from zero), $A_k$ is the amplitude of harmonic $k$, and $\phi_k$ is its angular phase. +For simplicity, if \\(x(t)\\) is a periodic signal with \\(f=1\\), then +\\[ x(t) = A_0 + \sum_{k=1}^\infty A_k \cos \left( 2\pi k t + \phi_k \right) \\] +where \\(A_0\\) is the *DC component* (the signal's average offset from zero), \\(A_k\\) is the amplitude of harmonic \\(k\\), and \\(\phi_k\\) is its angular phase. Each term of the sum is a simple harmonic signal, and when superimposed, they reconstruct the original signal. As it turns out, not only periodic signals but *all* signals have a unique decomposition into shifted cosines. -However, instead of integer harmonics, you must consider all frequencies $f$, and instead of a discrete sum, you must integrate over all shifted cosines of real-numbered frequencies. -Furthermore, it is easier to combine the amplitude and phase into a single complex-valued component $X(f) = A(f) e^{i \phi(f)}$. +However, instead of integer harmonics, you must consider all frequencies \\(f\\), and instead of a discrete sum, you must integrate over all shifted cosines of real-numbered frequencies. +Furthermore, it is easier to combine the amplitude and phase into a single complex-valued component \\(X(f) = A(f) e^{i \phi(f)}\\). With these modifications, the decomposition becomes -$$ x(t) = \int_{-\infty}^\infty X(f) e^{2\pi i t f} df $$ -The $X(f)$ component can be calculated with -$$ X(f) = \int_{-\infty}^\infty x(t) e^{-2\pi i t f} dt $$ +\\[ x(t) = \int_{-\infty}^\infty X(f) e^{2\pi i t f} df \\] +The \\(X(f)\\) component can be calculated with +\\[ X(f) = \int_{-\infty}^\infty x(t) e^{-2\pi i t f} dt \\] This is known as the [Fourier transform](https://en.wikipedia.org/wiki/Fourier_transform). -For a digital periodic signal with period $N$, no harmonics above $N/2$ can be represented, so its decomposition is truncated. -$$ x_k = \sum_{n=0}^{N/2} X_n e^{2\pi i kn / N} $$ +For a digital periodic signal with period \\(N\\), no harmonics above \\(N/2\\) can be represented, so its decomposition is truncated. +\\[ x_k = \sum_{n=0}^{N/2} X_n e^{2\pi i kn / N} \\] Its inverse is -$$ X_n = \sum_{k=0}^{N} x_k e^{-2\pi i kn / N} $$ -Since the sequence $x_k$ is real-valued, the components $X_n$ are conjugate symmetric across $N/2$, i.e. $X_n = X_{N-n}^\ast$, so we have $N/2 + 1$ unique harmonic components. +\\[ X_n = \sum_{k=0}^{N} x_k e^{-2\pi i kn / N} \\] +Since the sequence \\(x_k\\) is real-valued, the components \\(X_n\\) are conjugate symmetric across \\(N/2\\), i.e. \\(X_n = X_{N-n}^\ast\\), so we have \\(N/2 + 1\\) unique harmonic components. This is the (real) [discrete Fourier transform](https://en.wikipedia.org/wiki/Discrete_Fourier_transform) (DFT). -The [fast Fourier transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform) (FFT) is a method that surprisingly computes the DFT of a block of $N$ samples in $O(N \log N)$ steps rather than $O(N^2)$ like the DFT by using a recursive [dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming) algorithm, while being more numerically stable than the naive sum. +The [fast Fourier transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform) (FFT) is a method that surprisingly computes the DFT of a block of \\(N\\) samples in \\(O(N \log N)\\) steps rather than \\(O(N^2)\\) like the DFT by using a recursive [dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming) algorithm, while being more numerically stable than the naive sum. This makes the DFT feasible in high-performance DSP when processing blocks of samples. The larger the block size, the lower the average number of steps required per sample. FFT algorithms exist in many libraries, like [pffft](https://bitbucket.org/jpommier/pffft/) used in VCV Rack itself. @@ -67,11 +67,11 @@ FFT algorithms exist in many libraries, like [pffft](https://bitbucket.org/jpomm ### Sampling -The [Nyquist–Shannon sampling theorem](https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem) states that a signal with no frequency components higher than half the sample rate $f_{sr}$ can be sampled and reconstructed without losing information. -In other words, if you bandlimit a signal (with a brickwall lowpass filter at $f_{sr}/2$) and sample points at $f_{sr}$, you can reconstruct the bandlimited signal by finding the unique signal which passes through all points and has no frequency components higher than $f_{sr}/2$. +The [Nyquist–Shannon sampling theorem](https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem) states that a signal with no frequency components higher than half the sample rate \\(f_{sr}\\) can be sampled and reconstructed without losing information. +In other words, if you bandlimit a signal (with a brickwall lowpass filter at \\(f_{sr}/2\\)) and sample points at \\(f_{sr}\\), you can reconstruct the bandlimited signal by finding the unique signal which passes through all points and has no frequency components higher than \\(f_{sr}/2\\). -In practice, digital-to-analog converters (DACs) apply an approximation of a brickwall lowpass filter to remove frequencies higher than $f_{sr}/2$ from the signal. -The signal is integrated for a small fraction of the sample time $1/f_{sr}$ to obtain an approximation of the amplitude at a point in time, and this measurement is quantized to the nearest digital value. +In practice, digital-to-analog converters (DACs) apply an approximation of a brickwall lowpass filter to remove frequencies higher than \\(f_{sr}/2\\) from the signal. +The signal is integrated for a small fraction of the sample time \\(1/f_{sr}\\) to obtain an approximation of the amplitude at a point in time, and this measurement is quantized to the nearest digital value. Analog-to-digital converters (ADCs) convert a digital value to an amplitude and hold it for a fraction of the sample time. A [reconstruction filter](https://en.wikipedia.org/wiki/Reconstruction_filter) is applied, producing a signal close to the original bandlimited signal. @@ -84,7 +84,7 @@ Fortunately, modern DACs and ADCs as cheap as $2-5 per chip can digitize and ### Aliasing -The Nyquist–Shannon sampling theorem requires the original signal to be bandlimited at $f_{sr}/2$ before digitizing. +The Nyquist–Shannon sampling theorem requires the original signal to be bandlimited at \\(f_{sr}/2\\) before digitizing. If it is not, reconstructing will result in an entirely different signal, which usually sounds ugly and is associated with poor-quality DSP. Consider the high-frequency sine wave in red. @@ -93,16 +93,16 @@ If correctly bandlimited, the original signal would be zero (silence), and thus [![](https://upload.wikimedia.org/wikipedia/commons/2/28/AliasingSines.svg)](https://en.wikipedia.org/wiki/File:AliasingSines.svg) -A square wave has harmonic amplitudes $\frac{1}{k}$ for odd harmonics $k$. -However, after bandlimiting, all harmonics above $f_{sr}/2$ become zero, so its reconstruction should look like this. +A square wave has harmonic amplitudes \\(\frac{1}{k}\\) for odd harmonics \\(k\\). +However, after bandlimiting, all harmonics above \\(f_{sr}/2\\) become zero, so its reconstruction should look like this. [![](https://upload.wikimedia.org/wikipedia/commons/b/b3/Gibbs_phenomenon_50.svg)](https://en.wikipedia.org/wiki/File:Gibbs_phenomenon_50.svg) The curve produced by a bandlimited discontinuity is known as the [Gibbs phenomenon](https://en.wikipedia.org/wiki/Gibbs_phenomenon). A DSP algorithm attempting to model a jump found in sawtooth or square waves must include this effect, such as by inserting a minBLEP or polyBLEP signal for each discontinuity. -Otherwise, higher harmonics, like the high-frequency sine wave above, will pollute the spectrum below $f_{sr}/2$. +Otherwise, higher harmonics, like the high-frequency sine wave above, will pollute the spectrum below \\(f_{sr}/2\\). -Even signals containing no discontinuities, such as a triangle wave with harmonic amplitudes $(-1)^k / k^2$, must be correctly bandlimited or aliasing will occur. +Even signals containing no discontinuities, such as a triangle wave with harmonic amplitudes \\((-1)^k / k^2\\), must be correctly bandlimited or aliasing will occur. One possible method is to realize that a triangle wave is an integrated square wave, and an integrator is just a filter with a -20dB per [decade](https://en.wikipedia.org/wiki/Decade_%28log_scale%29) slope. Since linear filters commute, a bandlimited integrated square wave is just an integrated bandlimited square wave. @@ -116,89 +116,89 @@ It is sometimes *not* required for reverb, linear filters, audio-rate FM of sine ### Linear filters A linear filter is a operation that applies gain depending on a signal's frequency content, defined by -$$Y(s) = H(s) X(s)$$ -where $s = i \omega$ is the complex angular frequency, $X$ and $Y$ are the [Laplace transforms](https://en.wikipedia.org/wiki/Laplace_transform) of the input signal $x(t)$ and output signal $y(t)$, and $H(s)$ is the [transfer function](https://en.wikipedia.org/wiki/Transfer_function) of the filter, defining its character. +\\[Y(s) = H(s) X(s)\\] +where \\(s = i \omega\\) is the complex angular frequency, \\(X\\) and \\(Y\\) are the [Laplace transforms](https://en.wikipedia.org/wiki/Laplace_transform) of the input signal \\(x(t)\\) and output signal \\(y(t)\\), and \\(H(s)\\) is the [transfer function](https://en.wikipedia.org/wiki/Transfer_function) of the filter, defining its character. Note that the [Fourier transform](https://en.wikipedia.org/wiki/Fourier_transform) is not used because of time causality, i.e. we do not know the future of a signal. The filter is "linear" because the filtered sum of two signals is equal to the sum of the two individually filtered signals. -A log-log plot of $H(i \omega)$ is called a [Bode plot](https://en.wikipedia.org/wiki/Bode_plot). +A log-log plot of \\(H(i \omega)\\) is called a [Bode plot](https://en.wikipedia.org/wiki/Bode_plot). [![](https://upload.wikimedia.org/wikipedia/commons/6/60/Butterworth_response.svg)](https://en.wikipedia.org/wiki/File:Butterworth_response.svg) -To be able to exploit various mathematical tools, the transfer function is often written as a rational function in terms of $s^{-1}$ -$$H(s) = \frac{\sum_{p=0}^P b_p s^{-p}}{\sum_{q=0}^Q a_q s^{-q}}$$ -where $a_q$ and $b_p$ are called the *analog filter coefficients*. -With sufficient orders $P$ and $Q$ of the numerator and denominator polynomial, you can approximate most linear analog filters found in synthesis. +To be able to exploit various mathematical tools, the transfer function is often written as a rational function in terms of \\(s^{-1}\\) +\\[H(s) = \frac{\sum_{p=0}^P b_p s^{-p}}{\sum_{q=0}^Q a_q s^{-q}}\\] +where \\(a_q\\) and \\(b_p\\) are called the *analog filter coefficients*. +With sufficient orders \\(P\\) and \\(Q\\) of the numerator and denominator polynomial, you can approximate most linear analog filters found in synthesis. -To digitally implement a transfer function, define $z$ as the operator that transforms a sample $x_n$ to its following sample, i.e. $x_{n+1} = z[x_n]$. -We can actually write this as a variable in terms of $s$ and the sample time $T = 1/f_{sr}$. -(Consider a sine wave with angular frequency $\omega$. The $z$ operator shifts its phase as if we delayed by $T$ time.) -$$z = e^{sT}$$ +To digitally implement a transfer function, define \\(z\\) as the operator that transforms a sample \\(x_n\\) to its following sample, i.e. \\(x_{n+1} = z[x_n]\\). +We can actually write this as a variable in terms of \\(s\\) and the sample time \\(T = 1/f_{sr}\\). +(Consider a sine wave with angular frequency \\(\omega\\). The \\(z\\) operator shifts its phase as if we delayed by \\(T\\) time.) +\\[z = e^{sT}\\] A first order approximation of this is -$$z = \frac{e^{sT/2}}{e^{-sT/2}} \approx \frac{1 + sT/2}{1 - sT/2}$$ +\\[z = \frac{e^{sT/2}}{e^{-sT/2}} \approx \frac{1 + sT/2}{1 - sT/2}\\] and its inverse is -$$s = \frac{1}{T} \ln{z} \approx \frac{2}{T} \frac{1 - z^{-1}}{1 + z^{-1}}$$ +\\[s = \frac{1}{T} \ln{z} \approx \frac{2}{T} \frac{1 - z^{-1}}{1 + z^{-1}}\\] This is known as the [Bilinear transform](https://en.wikipedia.org/wiki/Bilinear_transform). In digital form, the rational transfer function is written as -$$H(z) = \frac{\sum_{n=0}^N b_n z^{-n}}{\sum_{m=0}^M a_m z^{-m}}$$ -Note that the orders $N$ and $M$ are not necessarily equal to the orders $P$ and $Q$ of the analog form, and we obtain a new set of numbers $a_m$ and $b_n$ called the *digital filter coefficients*. +\\[H(z) = \frac{\sum_{n=0}^N b_n z^{-n}}{\sum_{m=0}^M a_m z^{-m}}\\] +Note that the orders \\(N\\) and \\(M\\) are not necessarily equal to the orders \\(P\\) and \\(Q\\) of the analog form, and we obtain a new set of numbers \\(a_m\\) and \\(b_n\\) called the *digital filter coefficients*. -The *zeros* of the filter are the nonzero values of $z$ which give a zero numerator, and the *poles* are the nonzero values of $z$ which give a zero denominator. -A linear filter is stable (its [impulse response](https://en.wikipedia.org/wiki/Impulse_response) converges to 0) if and only if all poles lie strictly within the complex unit circle, i.e. $|z| < 1$. +The *zeros* of the filter are the nonzero values of \\(z\\) which give a zero numerator, and the *poles* are the nonzero values of \\(z\\) which give a zero denominator. +A linear filter is stable (its [impulse response](https://en.wikipedia.org/wiki/Impulse_response) converges to 0) if and only if all poles lie strictly within the complex unit circle, i.e. \\(|z| < 1\\). -We should now have all the tools we need to digitally implement any linear analog filter response $H(s)$ and vise-versa. +We should now have all the tools we need to digitally implement any linear analog filter response \\(H(s)\\) and vise-versa. #### IIR filters An infinite impulse response (IIR) filter is a digital filter that implements all possible rational transfer functions. -By multiplying the denominator of the rational $H(z)$ definition above on both sides and applying it to an input $x_k$ and output $y_k$, we obtain the difference relation -$$\sum_{m=0}^M a_m y_{k-m} = \sum_{n=0}^N b_n x_{k-n}$$ -Usually $a_0$ is normalized to 1, and $y_k$ can be written explicitly. -$$y_k = \sum_{n=0}^N b_n x_{k-n} - \sum_{m=1}^M a_m y_{k-m}$$ +By multiplying the denominator of the rational \\(H(z)\\) definition above on both sides and applying it to an input \\(x_k\\) and output \\(y_k\\), we obtain the difference relation +\\[\sum_{m=0}^M a_m y_{k-m} = \sum_{n=0}^N b_n x_{k-n}\\] +Usually \\(a_0\\) is normalized to 1, and \\(y_k\\) can be written explicitly. +\\[y_k = \sum_{n=0}^N b_n x_{k-n} - \sum_{m=1}^M a_m y_{k-m}\\] This iterative process can be computed for each sample -For $N, M = 2$, this is a [biquad filter](https://en.wikipedia.org/wiki/Digital_biquad_filter), which is very fast, numerically stable (assuming the transfer function itself is mathematical stable), and a reasonably good sounding filter. +For \\(N, M = 2\\), this is a [biquad filter](https://en.wikipedia.org/wiki/Digital_biquad_filter), which is very fast, numerically stable (assuming the transfer function itself is mathematical stable), and a reasonably good sounding filter. -$$H(z) = \frac{b_0 + b_1 z^{-1} + b_2 z^{-2}}{1 + a_1 z^{-1} + a_2 z^{-2}}$$ +\\[H(z) = \frac{b_0 + b_1 z^{-1} + b_2 z^{-2}}{1 + a_1 z^{-1} + a_2 z^{-2}}\\] #### FIR filters -A finite impulse response (FIR) filter is a specific case of an IIR filter with $M = 0$ (a transfer function denominator of 1). For an input and output signal, the difference relation is -$$y_k = \sum_{n=0}^N b_n x_{k-n}$$ +A finite impulse response (FIR) filter is a specific case of an IIR filter with \\(M = 0\\) (a transfer function denominator of 1). For an input and output signal, the difference relation is +\\[y_k = \sum_{n=0}^N b_n x_{k-n}\\] They are computationally straightforward and always stable since they have no poles. -Long FIR filters ($N \geq 128$) like FIR reverbs (yes, they are just linear filters) can be optimized through FFTs. -Note that the above formula is the convolution between vectors $y$ and $b$, and by the [convolution theorem](https://en.wikipedia.org/wiki/Convolution_theorem), -$$y \ast b = \mathcal{F}^{-1} \{ \mathcal{F}\{y\} \cdot \mathcal{F}\{b\} \}$$ -where $\cdot$ is element-wise multiplication. +Long FIR filters (\\(N \geq 128\\)) like FIR reverbs (yes, they are just linear filters) can be optimized through FFTs. +Note that the above formula is the convolution between vectors \\(y\\) and \\(b\\), and by the [convolution theorem](https://en.wikipedia.org/wiki/Convolution_theorem), +\\[y \ast b = \mathcal{F}^{-1} \{ \mathcal{F}\{y\} \cdot \mathcal{F}\{b\} \}\\] +where \\(\cdot\\) is element-wise multiplication. -While the naive FIR formula above requires $O(N)$ computations per sample, the FFT FIR method requires an average of $O(\log N)$ computations per sample when processing blocks of $N$ samples. -A disadvantage of the FFT FIR method is that the signal must be delayed by $N$ samples before any output can be determined. -You can combine the naive and FFT methods into a hybrid approach with the [overlap-add](https://en.wikipedia.org/wiki/Overlap%E2%80%93add_method) or [overlap-save](https://en.wikipedia.org/wiki/Overlap%E2%80%93save_method) methods, allowing you to process smaller blocks of size $M < N$ at a slightly worse $O((N/M) \log M)$ average computations per sample. +While the naive FIR formula above requires \\(O(N)\\) computations per sample, the FFT FIR method requires an average of \\(O(\log N)\\) computations per sample when processing blocks of \\(N\\) samples. +A disadvantage of the FFT FIR method is that the signal must be delayed by \\(N\\) samples before any output can be determined. +You can combine the naive and FFT methods into a hybrid approach with the [overlap-add](https://en.wikipedia.org/wiki/Overlap%E2%80%93add_method) or [overlap-save](https://en.wikipedia.org/wiki/Overlap%E2%80%93save_method) methods, allowing you to process smaller blocks of size \\(M < N\\) at a slightly worse \\(O((N/M) \log M)\\) average computations per sample. ### Impulse responses Sometimes we need to simulate non-rational transfer functions. -Consider a general transfer function $H(f)$, written in terms of $f$ rather than $s$ for simplicity. -Our original definition of a transfer function for an input $x(t)$ and output $y(t)$ is -$$ Y(f) = H(f) X(f) $$ +Consider a general transfer function \\(H(f)\\), written in terms of \\(f\\) rather than \\(s\\) for simplicity. +Our original definition of a transfer function for an input \\(x(t)\\) and output \\(y(t)\\) is +\\[ Y(f) = H(f) X(f) \\] and by applying the convolution theorem again, we obtain -$$ y(t) = (h \ast x)(t) = \int_{-\infty}^\infty h(\tau) x(t - \tau) d\tau $$ -where $h(t)$ is the *impulse response* of our filter. +\\[ y(t) = (h \ast x)(t) = \int_{-\infty}^\infty h(\tau) x(t - \tau) d\tau \\] +where \\(h(t)\\) is the *impulse response* of our filter. -The signal $h(t)$ is the result of processing a [delta function](https://en.wikipedia.org/wiki/Dirac_delta_function) through our filter, since $\delta(t)$ is the "identity" function of convolution, i.e. $h(t) = (h \ast \delta)(t)$. -Clapping your hands or popping a balloon (both good approximations of $\delta$) in a large cathedral will generate a very sophisticated impulse response, which can be recorded as $h(t)$ and processed in a FIR filter algorithm to reproduce arbitrary sounds as if they were performed in the cathedral. +The signal \\(h(t)\\) is the result of processing a [delta function](https://en.wikipedia.org/wiki/Dirac_delta_function) through our filter, since \\(\delta(t)\\) is the "identity" function of convolution, i.e. \\(h(t) = (h \ast \delta)(t)\\). +Clapping your hands or popping a balloon (both good approximations of \\(\delta\\)) in a large cathedral will generate a very sophisticated impulse response, which can be recorded as \\(h(t)\\) and processed in a FIR filter algorithm to reproduce arbitrary sounds as if they were performed in the cathedral. Repeating this process in the digital realm gives us the discrete convolution. -$$ y_k = \sum_{n=-\infty}^\infty h_n x_{k-n} $$ +\\[ y_k = \sum_{n=-\infty}^\infty h_n x_{k-n} \\] #### Brickwall filter -An example of a non-rational transfer function is the ideal lowpass filter that fully attenuates all frequencies higher than $f_c$ and passes all frequencies below $f_c$. +An example of a non-rational transfer function is the ideal lowpass filter that fully attenuates all frequencies higher than \\(f_c\\) and passes all frequencies below \\(f_c\\). Including [negative frequencies](https://en.wikipedia.org/wiki/Negative_frequency), its transfer function is $$ H(f) = \begin{cases} @@ -206,20 +206,20 @@ H(f) = \begin{cases} 0 & \text{otherwise} \end{cases} $$ -The inverse Fourier transform of $H(f)$ is -$$ h(t) = 2 f_c \operatorname{sinc}(2 f_c t) $$ -where $\operatorname{sinc}(x) = \sin(\pi x) / (\pi x)$ is the [normalized sinc function](https://en.wikipedia.org/wiki/Sinc_function). -Although the impulse response is infinitely long, restricting it to a finite range $[-T, T]$ and shifting it forward by $T$ produces a finite causal impulse response that can be solved by a fast FIR algorithm to produce a close approximation of an ideal brickwall filter. +The inverse Fourier transform of \\(H(f)\\) is +\\[ h(t) = 2 f_c \operatorname{sinc}(2 f_c t) \\] +where \\(\operatorname{sinc}(x) = \sin(\pi x) / (\pi x)\\) is the [normalized sinc function](https://en.wikipedia.org/wiki/Sinc_function). +Although the impulse response is infinitely long, restricting it to a finite range \\([-T, T]\\) and shifting it forward by \\(T\\) produces a finite causal impulse response that can be solved by a fast FIR algorithm to produce a close approximation of an ideal brickwall filter. ### Windows -The impulse response $h_n$ is defined for all integers $n$, so it is both non-causal (requires future knowledge of $x(t)$ to compute $y(t)$) and infinitely long. +The impulse response \\(h_n\\) is defined for all integers \\(n\\), so it is both non-causal (requires future knowledge of \\(x(t)\\) to compute \\(y(t)\\)) and infinitely long. *TODO* -### To-do +## To-do - digital filters - windows diff --git a/Installing.md b/Installing.md index 4701126..47057c2 100644 --- a/Installing.md +++ b/Installing.md @@ -28,6 +28,6 @@ If your computer is offline, you may download plugins using another computer and *Install third-party plugins at your own risk. Like VST plugins, installing plugins from unknown sources may compromise your computer and personal information.* -Download the plugin ZIP package from the vendor's website to `/plugins` (See [Where is the "Rack local directory"?](FAQ.md#where-is-the-rack-local-directory)). Rack will extract and load the plugin upon launch. +Download the plugin ZIP package from the vendor's website to `/plugins` (See [Where is the "Rack local directory"?](FAQ.html#where-is-the-rack-local-directory)). Rack will extract and load the plugin upon launch. Note: Do not download the plugin via GitHub's green "Clone or download" button. These are source code ZIP packages and do not contain the compiled plugin binary. If the vendor distributes their plugin with GitHub, look in the "Releases" section for the compiled ZIP packages. diff --git a/Introduction.md b/Introduction.md deleted file mode 100644 index 2092d0c..0000000 --- a/Introduction.md +++ /dev/null @@ -1,26 +0,0 @@ -# VCV Rack Manual - -The VCV Rack manual is under active development. - -Edit this manual at https://github.com/VCVRack/manual. - - -### Communities - -- Website: https://vcvrack.com/ -- Facebook: https://www.facebook.com/vcvrack/ -- Twitter: https://twitter.com/vcvrack -- Github issue tracker (features, bugs, and developer discussions): https://github.com/VCVRack/Rack/issues -- Facebook group: https://www.facebook.com/groups/vcvrack/ -- Facebook developer group: https://www.facebook.com/groups/2035785263299933/ -- Facebook French group: https://www.facebook.com/groups/2069785583250645/ -- Switched On Rack blog: http://www.switchedonrack.com/ -- MuffWiggler thread: https://www.muffwiggler.com/forum/viewtopic.php?t=186899 -- KVR Audio thread: https://www.kvraudio.com/forum/viewtopic.php?f=23&t=489230 -- Hispasonic thread (Español): https://www.hispasonic.com/foros/foro-vcv-rack/516252 -- Patchstorage: https://patchstorage.com/platform/vcv-rack/ -- Discord: https://discord.gg/wxa89Mh -- Reddit: https://www.reddit.com/r/vcvrack/ -- Twitch: https://www.twitch.tv/communities/vcvrackcommunity -- VCV Rack Developers Slack: [vcvrackdevelopers.slack.com](https://vcvrackdevelopers.slack.com/join/shared_invite/enQtMzczNzY2NDUzMTczLWM2Mjg0ZjEzNDQ2YTEwYjFiZTA3MzE4NTRjMjg5ZjRkZDAwMDk5M2I4NmIzYmEyZGY1NGQ4YWE4NzkzZjlhMmI) -- IRC: http://freenode.net/ #VCVRack diff --git a/Makefile b/Makefile index 26a924b..381cc3c 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,13 @@ +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SOURCEDIR = . +BUILDDIR = _build -build: - gitbook build -all: build +all: html -run: - gitbook serve --port 8080 +upload: html + rsync _build/html vcvrack.com:vcvrack.com/manual/ -ruvz --delete -pdf: build - gitbook pdf . _book/VCV-Rack-manual.pdf - -upload: all - rsync _book/ vcvrack.com:vcvrack.com/manual/ -ruvz --delete +%: + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) diff --git a/README.md b/README.md index 5b7a3d2..c1f0a27 100644 --- a/README.md +++ b/README.md @@ -8,22 +8,12 @@ Send a pull request to this repository with your edits. Major changes like new pages and complete overhauls are welcome, as well as minor fixes like grammar, spelling, and reorganization. Your PR will be accepted if it is a net positive benefit to readers. -The LaTeX renderer supports [these functions](https://khan.github.io/KaTeX/function-support.html). - ### Building -Install [Node](https://nodejs.org/en/). Make sure `npm` is in your PATH. - -Install [gitbook](https://github.com/GitbookIO/gitbook) with - - npm install gitbook-cli -g - -Install gitbook plugins with - - gitbook install +Install [Sphinx](http://www.sphinx-doc.org/en/stable/). Build with - make run + make html -and preview the manual at http://localhost:8080/. +The output should be generated in `_build/html`. diff --git a/TOC.md b/TOC.md deleted file mode 100644 index 8aa897f..0000000 --- a/TOC.md +++ /dev/null @@ -1,24 +0,0 @@ -# Table of Contents - -### Rack - -- [Quick Start](QuickStart.md) -- [Installing](Installing.md) -- [FAQ](FAQ.md) -- [Toolbar](Toolbar.md) -- [Core](Core.md) -- [Bridge](Bridge.md) - -### Plugin Development -- [Tutorial](PluginDevelopmentTutorial.md) -- [Voltage Standards](VoltageStandards.md) -- [DSP](DSP.md) - -### Rack Development -- [Contributing](Contributing.md) -- [ABI/API Version](Version.md) - ---- - -- [Glossary](Glossary.md) -- [Edit on GitHub](https://github.com/VCVRack/manual) diff --git a/Toolbar.md b/Toolbar.md index 0f8b2bd..73e9b70 100644 --- a/Toolbar.md +++ b/Toolbar.md @@ -6,7 +6,7 @@ When power meters are enabled, Rack measures the amount of time spent processing In many ways, this is analogous to the module power limit imposed by hardware modular synthesizers in *mA* (milliamperes). To maintain a stable audio clock, the total amount of time spent processing all modules must equal **1000 mS**. -To achieve this, the [Audio](Core.md#audio) module from [Core](Core.md) uses your audio device's high-precision clock to regulate Rack's processing loop, so it idles for some amount of mS until this total is met. +To achieve this, the [Audio](Core.html#audio) module from [Core](Core.html) uses your audio device's high-precision clock to regulate Rack's processing loop, so it idles for some amount of mS until this total is met. If the Audio idle time falls close to an average of 0 mS over its block size, an audio stutter may occur. This can be caused by other modules consuming lots of mS. diff --git a/VoltageStandards.md b/VoltageStandards.md index 6a74599..676102a 100644 --- a/VoltageStandards.md +++ b/VoltageStandards.md @@ -36,7 +36,7 @@ Gates should produce **10V** when active. ### Pitch Most Eurorack manufacturers use the **1V/octave** standard. -The relationship between frequency $f$ and voltage $V$ is $f = f_0 \cdot 2^{V}$, where $f_0$ is an arbitrary baseline set by a pitch knob or the note C4 (MIDI note 60, $f_0 =$ 261.626 Hz). +The relationship between frequency \\(f\\) and voltage \\(V\\) is \\(f = f_0 \cdot 2^{V}\\), where \\(f_0\\) is an arbitrary baseline set by a pitch knob or the note C4 (MIDI note 60, \\(f_0 =\\) 261.626 Hz). ### NaNs and Infinity diff --git a/_static/overrides.css b/_static/overrides.css new file mode 100644 index 0000000..44a3ac0 --- /dev/null +++ b/_static/overrides.css @@ -0,0 +1,13 @@ +@import url('https://fonts.googleapis.com/css?family=Nunito:300,300i,800'); + +body, h1, h2, .rst-content .toctree-wrapper p.caption, h3, h4, h5, h6, legend { + font-family: 'Nunito', sans-serif; +} + +.wy-side-nav-search { + background-color: transparent; +} + +.wy-nav-content { + max-width: 1000px; +} \ No newline at end of file diff --git a/book.json b/book.json deleted file mode 100644 index 3cbadcf..0000000 --- a/book.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "title": "VCV Rack Manual", - "description": "Documentation for VCV Rack, the open-source virtual modular synthesizer", - "author": "VCV", - "language": "en", - "plugins": [ - "insert-logo-link", - "github-edit", - "katex-plus", - "-sharing" - ], - "pluginsConfig": { - "insert-logo-link": { - "src": "images/logo.png", - "url": "https://vcvrack.com/" - }, - "github-edit": { - "repo": "VCVRack/manual", - "branch": "master" - } - }, - "structure": { - "readme": "Introduction.md", - "summary": "TOC.md", - "glossary": "Glossary.md" - }, - "pdf": { - "fontSize": 12, - "fontFamily": "DejaVu Sans", - "paperSize": "a5" - } -} \ No newline at end of file diff --git a/conf.py b/conf.py new file mode 100644 index 0000000..8b7b7f5 --- /dev/null +++ b/conf.py @@ -0,0 +1,99 @@ + +project = 'VCV Rack' +copyright = '2018, VCV' +author = 'VCV' + +source_encoding = 'utf-8' + + +extensions = [ + 'sphinx.ext.mathjax', +] + +templates_path = ['_templates'] + +from recommonmark.parser import CommonMarkParser + +source_parsers = { + '.md': CommonMarkParser, +} + +source_suffix = ['.rst', '.md'] + +master_doc = 'index' + +language = 'en' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path . +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# Theme + +html_theme = "sphinx_rtd_theme" + +html_theme_options = { + 'logo_only': True, # if we have a html_logo below, this shows /only/ the logo with no title text + 'collapse_navigation': False, # Collapse navigation (False makes it tree-like) +} + +html_context = { + "display_github": True, # Integrate GitHub + "github_user": "VCVRack", # Username + "github_repo": "manual", # Repo name + "github_version": "master", # Version + "conf_py_path": "/", # Path in the checkout to the docs root +} + +html_logo = 'images/logo.png' + +html_static_path = ['_static'] + +html_show_sphinx = False +html_show_copyright = False + +htmlhelp_basename = 'VCVRackdoc' + + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +latex_documents = [ + (master_doc, 'VCVRack.tex', 'VCV Rack Manual Documentation', + 'VCV', 'manual'), +] + + +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright +epub_exclude_files = ['search.html'] + + +def setup(app): + app.add_stylesheet("overrides.css") + app.add_config_value('recommonmark_config', { + 'enable_math': True, + 'enable_inline_math': True + }, True) diff --git a/images/Bridge Live.png b/images/BridgeLive.png similarity index 100% rename from images/Bridge Live.png rename to images/BridgeLive.png diff --git a/images/logo.png b/images/logo.png index 3cf162992ffffa38c2db0003e8d247496602734b..c1debd08693c977e2047eeb98c2eec5434c6be0e 100644 GIT binary patch delta 1071 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 diff --git a/index.rst b/index.rst new file mode 100644 index 0000000..9c48e91 --- /dev/null +++ b/index.rst @@ -0,0 +1,45 @@ + +VCV Rack Manual +=========================================== + +test :math:`a^2 + b^2 = c^2` test + +The VCV Rack manual is under active development. + +Edit this manual at https://github.com/VCVRack/manual. + +Table of Contents +---------------------- + +.. toctree:: + :maxdepth: 1 + + QuickStart.md + Installing.md + FAQ.md + Toolbar.md + Core.md + Bridge.md + +.. toctree:: + :maxdepth: 1 + :caption: Plugin Development + + PluginDevelopmentTutorial.md + VoltageStandards.md + DSP.md + +.. toctree:: + :maxdepth: 1 + :caption: Rack Development + + Contributing.md + Version.md + +.. toctree:: + :maxdepth: 1 + :caption: Index + + Communities.md + Glossary.md + https://github.com/VCVRack/manual