# Programming Real-Time Sound in Python
## 4 Applications to Virtual Analog
### Ring Modulator  
The diode-based ring modulator model is known to be computationally demanding when realized in the digital domain. A popular model [29] of this musical circuit, see Algorithm 1, asks for computing three non-linear and six linear difference equations at every incoming pair of signal samples, \(v_{m}\) and \(v_{c}\). Non-linearity is expressed by the voltage-to-current characteristic \(g(v)=0.17v^{4}\) if \(v>0\), and zero otherwise.  
To avoid instability, the digital input needs to be oversampled by a factor of at least 25 times the standard audio rate. The Python interpreter falls short in achieving real time as it computes the above equations at about one thirtieth of the audio rate, hence hundreds of times as slow as it should, as it can be seen from Table 1. As expected, an analysis made with _line_profiler_ shows that most of the time is spent to repeatedly iterate over the instructions computing the circuit equations.  
Numba hits the goal instead, enabling reliable real-time processing, as reported in Table 1. The related refactoring (the pseudo-code is shown in Listing A7) consists of the creation of a wrap function _compute_numba()_, passing the global variables to the compiled function _compute_numba_low()_ that computes the circuit equations. Moreover, the compiler is instructed about the static character of the input and return arguments.  
The easiest Cython refactoring of the program consists of moving the computing function _compute()_ into an external _.pyx_ file, and, furthermore, declaring the static variables sent to compilation using the _cdef_ annotation. The rest of the code is left to standard interpretation. Cython enables real-timeprocessing almost as efficiently as Numba. The corresponding pseudo-code for Cython is shown in Listing A8. The code chunks appearing in Listings A9, A10, and A11, show the refactoring using an external C file. This file contains the instructions describing the circuit equations. The translation in C is straightforward, since no additional libraries are needed for computing the related mathematical expressions. The final software architecture consists of _c_diode.c_ and _diode.pyx_, forming the algorithm library, and the main file _test_diode.py_ that calls the external functions importing the _diode_ module. As expected, the manual transcription in C of the equations yields almost the same performance as the previous cythonization.  
```
1:repeat
2: read \(v_{m}\), \(v_{c}\)
3: compute \(v_{1}=\frac{R_{m}}{1+CH_{5}R_{m}}\Big{(}\frac{v_{m}}{R_{m}}+i_{1}-\frac{g(v_{ 4})}{2}+\frac{g(v_{5})}{2}-\frac{g(v_{6})}{2}+\frac{g(v_{2})}{2}\Big{)}+v_{1}\)
4: compute \(v_{2}=\frac{R_{m}}{1+CH_{5}R_{m}}\Big{(}i_{2}+\frac{g(v_{4})}{2}-\frac{g(v_{ 5})}{2}-\frac{g(v_{6})}{2}+\frac{g(v_{7})}{2}\Big{)}+v_{2}\)
5: compute \(v_{3}=\frac{R_{m}}{1+CH_{5}R_{m}}\Big{(}g(v_{4})+g(v_{5})-g(v_{6})-g(v_{7}) \Big{)}+v_{3}\)
6: compute \(v_{4}=\frac{v_{1}}{2}-\frac{v_{2}}{2}-v_{3}-v_{c}\)
7: compute \(v_{5}=-\frac{v_{1}}{2}+\frac{v_{2}}{2}-v_{3}-v_{c}\)
8: compute \(v_{6}=\frac{v_{1}}{2}+\frac{v_{2}}{2}+v_{3}+v_{c}\)
9: compute \(v_{7}=-\frac{v_{1}}{2}-\frac{v_{2}}{2}+v_{3}+v_{c}\)
10: compute \(i_{1}=-\frac{1}{CH_{5}}v_{1}+i_{1}\)
11: compute \(i_{2}=-\frac{1}{CH_{5}}v_{2}+i_{2}\)
12:until ever.
```  
**Algorithm 1**Ring modulator core processing loop.  
Table 1 also reports the memory requirements. In particular, Numba and Cython require more space than interpretation. This space considers only the running application; the overall Python environment needs almost 147 MB of memory.