Simple FM Matrix

MUMT-307 Final Project by Run Xuan Yang

Downloads

Please refer to Simple FM Matrix CodePlex webpage for binary and source distributions.

Dependencies

- Windows Vista SP2 or later
- Microsoft .NET Framework 4.5
- ASIO Driver

Overview

Simple FM Matrix is a basic implementation of frequency modulation sound synthesizer. It has 6 operators, each with its own ADSR envelope and frequency. Modulation indices are modified via the FM Matrix, which supports multiple modulator FM and feedback FM. Specifically, the program features

- 6 operators, each with its own frequency and amplitude envelope control;
- An FM Matrix in which one can construct algorithms by modifying modulation indices and output levels;
- Waveform and spectrum preview;
- Virtual piano keyboard for sound testing, played via either mouse clicks or keyboard shortcuts;
- Conversion from MIDI to wave file.

The program is implemented in WPF using MVVM design pattern, and contains 2300 lines of C# / VB / XAML code and 340 lines of F# code. Here is a list of libraries used in the program:

- NAudio for sound output;
- WPF Shell Integration Library for building a chromeless window;
- Math.NET Numerics for Fast Fourier Transforms;
- and a MIDI file decoder I have written as part of the MUMT-306 final project .

Some Math Behind

Discrete FM formula:

$$x_{i, n} = E_{i, n} \sin \! \left( \frac{2 \pi f_i n}{T_s} + \sum_{j=0}^{i-1} \frac{I_{i, j} \, x_{j, n}}{E_{j,n}} + \sum_{j=i}^{p-1} \frac{I_{i, j} \, x_{j, n-1}}{E_{j, n-1}} \right)$$

$$y_n = \sum_{i=0}^{p-1} L_i x_{i,n}$$

$ \begin{array}{lll} \mbox{where} & y_{n}\mbox{:} & \mbox{output waveform at sample index $n$} \\ & x_{i, n}\mbox{:} & \mbox{waveform of operator $i$ at sample index $n$} \\ & L_{i}\mbox{:} & \mbox{output level of operator $i$} \\ & E_{i, n}\mbox{:} & \mbox{envelope of operator $i$ at sample index $n$} \\ & I_{i, j}\mbox{:} & \mbox{modulation index of operator $j$ over operator $i$} \\ & f_{i}\mbox{:} & \mbox{frequency of operator $i$} \\ & p\mbox{:} & \mbox{number of operators} \\ & T_s\mbox{:} & \mbox{sampling period} \end{array} $

Pitch to frequency conversion:

$$f = 440 \cdot 2^{\frac{p-69}{12}}$$

$ \begin{array}{lll} \mbox{where} & p\mbox{:} & \mbox{pitch in MIDI note number} \\ & f\mbox{:} & \mbox{frequency in Hz} \end{array} $

Velocity to amplitude conversion:

$$A = \left(\frac{19}{2520} \, v + \frac{107}{2520} \right ) ^2$$

$ \begin{array}{lll} \mbox{where} & v\mbox{:} & \mbox{MIDI note velocity} \\ & A\mbox{:} & \mbox{peak amplitude} \end{array} $

Issues

When converting a MIDI file to a wave file, one may notice the problem that notes of higher pitch is significantly louder than notes of lower pitch, even when their dynamics are the same or the converse. This is because I haven't found a good approximation to Equal Loudness Contours yet, and it is left as one of the future improvements to the program.

The model part of the MVVM pattern is written in F#, because type definitions in F# is in general shorter than in other languages. However, since FM synthesis is so computationally intensive that even a function call could make a perceivable difference in performance, higher order functions are quite impractical and as a result, most of the code still uses imperative programming paradigm like mutable variables and for / while loops.

Unfortunately I wasn't able to use C++, which gives me easier access to libraries like FFTW and RtAudio as well as a performance boost, primarily because my knowledge to C++ is too little that I can't even write a class definition and also because there is quite some workaround to get it to work with WPF.