# MT32/MUNT Instrument Analysis The best MT32 editor I have found is Ballade 2.5 on DOSbox (unsure public download location). - long https://www.midimusicadventures.com/queststudios/mt32-resource/utilities/#mainContent - short http://www.polynominal.com/roland-mt-32/index.html#edit - article http://www.muzines.co.uk/articles/enhancing-your-roland-mt32/3500 semi useful - forum https://www.vogons.org/viewforum.php?f=40&sid=def864f365fe2a771cab9c06191fbfe8 - list of control changes http://www.houseofsynth.com/hos-downloads/manuals/Roland/Roland-Misc-Docs/rwi-mt-32-mod.pdf ## Time Durations (Speed) Based on a python simplification of the MUNT C++ code (with extraneous things stripped away): "volume change per tick" is: - proportional to "total volume change" - halved every time "time += 8" This is an approximation of `time taken = 2 ** (time parameter/8)` with no relation to initial/final volume. Ticks occur at `32000 / INTERRUPT_TIME=7` hertz, where munt's INTERRUPT_TIME is not quite hardware-accurate. (I didn't find the constant of proportionality though.) The first phase (attack rise) can be set faster (or slower?) at loud note velocities. The other phases can be set faster (or slower?) for high notes. There needs to be a wiki for this. ### Ideas for refactoring MUNT source code - According to code comments, running MUNT at sample rates other than 32000Hz causes TVA ticks to occur at the wrong rate. Should this be fixed? - `LA32Ramp::startRamp()` has commented-out code `// (unsigned int)(EXP2F(((increment & 0x7F) + 24) / 8.0f) + 0.125f)`. This is a good approximation of the function's table-based return value, but it confused me at first. - Maybe it should be commented in a way to indicate "this is the formula the MT32 attempts to approximate". - I think this would make a good unit test, where you run both formulas side-by-side and ensure their return values are close. That way, as a reader, I know both formulas are nearly equivalent. - To me, good naming (including named static-types) is essential to understanding unfamiliar and familiar code. - Types.h: Add `LogIncrement8u` or `LogIncrementInt` data types, indicating "sign-magnitude log(dy/dt)" variables. - Maybe `LogSlope...` is better, if this actually indicates slope. - Also add comments at the site of definition, to indicate the exact nature of Increment variables. - Types.h: Add macro/function `#define NEG(logIncrement) (0x80 ^ (logIncrement))`, to clearly indicate "negate the direction of increment". - `const LogIncrementInt MAX_INCREMENT = 127;` - Change data type of all increment-style variables, including `startRamp(...increment...)`, to `LogIncrement*`. - `typedef int TVA_PHASE;` is minimally invasive. (Why didn't you give a name to `enum{TVA_PHASE_...}`? Maybe you lose the ability to compare enum entries?) - Add comments as to what the tables contain? eg: ```cpp // f(x) = 64 * log2(x/256) LogIncrement8u envLogarithmicTime[256]; // // 180-rotated exponential = 10 /¯¯¯ 4095 Bit16u exp9[512]; ``` ## Volume Levels I didn't figure out yet, but suspect output amplitude = TVA level^2. - just play a single test-note midi - amplification comes from `Bit32u Partial::getAmpValue()`. - Higher amplification values are louder. 0 is silent. It seems the volume curve can be customized. 100 is `velocity^(>2)`, 75 is `velocity^(≈1.9)`, 50 is constant, and I imagine 0 is `velocity^(<-2)`. I use 80 for my instruments, since it's approximates `velocity^(≈2)` as expected of General MIDI. ## PWM Range: 4/8 to 4/8 to 1/8 PWM=0 is square. PWM=50 is square. PWM=100 is 1/8 pulse wave. ## Sawtooth "PWM" I would never have guessed the mt32 implements "sawtooth" by inverting half of a triangle wave, and "sawtooth pwm" by inverting \<half of it. Oscilloscope screenshot (from corrscope): ![screenshot](https://cdn.discordapp.com/attachments/239478188961562625/581029631801425941/unknown.png) ## Pitch envelopes Apparently merely changing the duration of pitch envelopes (where all 4 pitch values are 0) is enough to leave 2 partials permanently out of sync. Is that because the MT32's sweep unit must be fed a nonzero slope over the duration? Trying to set up an exact phase shift between 2 partials (using pitch envelopes) is possible, but wildly inconsistent from note to note due to rounding. ## Phase Inversion I think partials (0 algo 1) have the same phase. (2 algo 3) are (sometimes but not always, what???) inverted relative to (0 algo 1). (0 algo 1) ± (2 algo 3). ## Reverb I think the reverb is fed by mono (left + right). The reverb processes the mono differently per ear, producing a "wide" stereo reverb. Proof of mono: when feeding it a "stereo-inverted signal" (feeding same partial into algorithms 1-1 and 8-1), I get no reverb.