Equalizer mid/side

Greetings to all! I’m doing a mid/side equalizer but I have a problem.I have divided the audio into mid/side, and I have the function to equalize already integrated. When decoding mid/side, the mid part of the audio moves to the left. I discovered that by applying preset 15 of the channel mixer plugin the audio returns to the original state.This is what I want to do: Decode the audio, equalize the mid part or the side part as needed, and finally re-apply the decoding function, to be able to leave the audio equalized and not change the stereo elements using this plug -in. First of all, Thanks.
Eq mid side.ny (827 Bytes)

Hello Steve and all the forum members. Today investigating a bit I could solve my problem that gave rise to this topic.Now I present my new plugin that has the following features:
Individual processing for the mid and side of the audio stereo.It contains different filters to choose: Parametric Equalizer, bass, treble, low pass filter and high pass filter.
Some tips of use:You can use it on stereo instruments tracks, reverb tracks, drum overheats, to highlight high frequencies a bit or to cut low frequencies.
It can be used in a complete mix with the parametric equalizer to cut some medium frequencies of the mid like 500 Hz.
Eq mid side.ny (1.68 KB)

Nice effective plug-in.

The plug-in works well, so the following are mostly details that (in my opinion) would be improvements:

Errors:

  • Spelling error in filter “type” options. Should be “Parametric”.
  • The “B” in “dB” should be capitalized. (It’s an abbreviation from “deci” and “Bel” https://en.wikipedia.org/wiki/Decibel)

Redundant parameters:
In your filter functions, you pass the variable “sig”, but it is not used in the function.

(defun filter (sig)

Indentation:
Although there are varying opinions about how to indent “cond” statements, (and some conflicting examples in the Nyquist source), it is generally accepted that indentation aids readability. For example, your filter code is written as:

(defun filter (sig)
(case type-mid
(0 (eq-band mid hz-mid gain-mid width-mid))
(1 (eq-lowshelf mid hz-mid gain-mid))
(2 (eq-highshelf mid hz-mid gain-mid))
(3 (lp mid hz-mid))
(t (hp mid hz-mid))))

whereas I would write it as:

(defun filter (sig type hz width gain)
  (case type
    (0 (eq-band sig hz width gain))
    (1 (eq-lowshelf sig hz width))
    (2 (eq-highshelf sig hz width))
    (3 (lowpass2 sig hz))
    (4 (highpass2 sig hz))
    (5 sig)))

Unnecessary processing when only one filter is required:
I’d add a filter option of “None” (bypass), so that the filter just returns “sig”, rather than having to calculate “(eq-band sig hz width 0.0)”. It also makes it more obvious how to achieve a “no-op” for one of the channels (For GUIs, “obvious” == “good” :slight_smile:)


Redundant code:
The functions “filter-mid” and “filter-side” are almost identical, but just with different parameters. It’s not much shorter, but I’d write it like this as it is clearer that both Mid and Side filters are essentially identical:

(defun filter-mid (sig)
  (filter sig type-mid hz-mid gain-mid width-mid))

(defun filter-side (sig)
  (filter sig type-side hz-side gain-side width-side))

(defun filter (sig type hz width gain)
  (case type
    (0 (eq-band sig hz width gain))
    (1 (eq-lowshelf sig hz width))
    (2 (eq-highshelf sig hz width))
    (3 (lowpass2 sig hz))
    (4 (highpass2 sig hz))
    (5 sig)))

You scale the signal twice. Once for the input:

(mult (aref *track* 0) 0.707099999)

and once for the output:

(mult (aref sig-encoded 0) 0.707099999)

Given that the overall effect simply scale for unity gain on two-way transcoding, you could simply scale (“mult”) the output by 0.5.


Multiple uses of code like this:

(vector (sum mid side)
        (sum mid (mult -1 side)))

I’d be inclined to wrap that in a reusable function which can be used for encoding and decoding.


Readability:
The algorithm for this effect is actually quite simple, though I think the code could express the algorithm more clearly. Readability is important, especially for fixing problems / maintaining the code. Rather than this (the main processing algorithm):

(let ((sig-encoded 
	(vector (filter-mid mid)
(filter-side side))))

(vector (sum
(mult (aref sig-encoded 0) 0.707099999)
(mult (aref sig-encoded 1) 0.707099999))
(sum
(mult (aref sig-encoded 0) 0.707099999)
(mult (aref sig-encoded 1) -0.707099999))))

I’d write it as:

(defun process (sig)
  (let* ((encoded (transcode sig))
         (mid  (filter-mid (aref encoded 0)))
         (side (filter-side (aref encoded 1))))
    ;; Decode and scale for unity gain two-way transcoding.
    (scale 0.5 (transcode mid side))))

Input validation:
For plug-ins intended for public distribution, it is always a good thing to validate user input. Not only does it provide help in the case of “user errors”, it can protect against unexpected code errors. The maxim is: “if it’s possible to do something wrong, someone will”.


Other:
Possibly not an “improvement”, but I’ve added it here as this feature is not yet documented:
There is a new “;control” available for Nyquist plug-ins in Audacity 2.3.0 (and later):

;control test "quoted string"

This simply adds a line of text to the GUI. Although it is good to avoid making the GUI too wordy, the addition of a line of text can sometimes add clarity.
In my version I have separated the filter sections with:

;control text "--- Mid channel filter ---"

and

;control text "--- Side channel filter ---"

You don’t have to agree with all of my changes, but this is my version of your plug-in, which (I hope) you may find useful:

;nyquist plug-in
;version 4
;type process
;preview linear
;name "Mid - Side Eq"
;action "Equalizing your audio..."
;author "Felipe Zanabria and Steve Daulton"
;release "0.2 beta"
;copyright "Released under terms of the GNU General Public License version 2"

;; For an explanation of Mid-Side encoding, see:
;; https://www.soundonsound.com/sound-advice/q-how-does-mid-sides-recording-actually-work

;control text "--- Mid channel filter ---"
;control type-mid "Mid filter type" choice "Parametrick eq,Low shelf,High shelf,Low pass filter,High pass filter,None" 0
;control hz-mid "Mid frequency" int "hz" 1000 100 10000
;control gain-mid "Mid gain" float "dB" 0 -24 24
;control width-mid "Mid bandwidth" float "Octaves" 0.5 0.01 3
;control text "--- Side channel filter ---"
;control type-side "Side filter type" choice "Parametric eq,Low shelf,High shelf,Low pass filter,High pass filter,None" 0
;control hz-side "Side frequency" int "hz" 1000 100 10000
;control gain-side "Side gain" float "dB" 0 -24 24
;control width-side "Side bandwidth" float "Octaves" 0.5 0.01 3


(defun filter-mid (sig)
  (filter sig type-mid hz-mid gain-mid width-mid))

(defun filter-side (sig)
  (filter sig type-side hz-side gain-side width-side))

(defun filter (sig type hz width gain)
  (case type
    (0 (eq-band sig hz width gain))
    (1 (eq-lowshelf sig hz width))
    (2 (eq-highshelf sig hz width))
    (3 (lowpass2 sig hz))
    (4 (highpass2 sig hz))
    (5 sig)))


(defun transcode (sig0 &optional sig1)
  ;;; Transcode stereo track or two channels from
  ;;; L/R to M/S or from M/S to L/R.
  (when (arrayp sig0)
    (setf sig1 (aref sig0 1))
    (setf sig0 (aref sig0 0)))
  (vector (sum sig0 sig1)
          (diff sig0 sig1)))


(defun process (sig)
  (let* ((encoded (transcode sig))
         (mid  (filter-mid (aref encoded 0)))
         (side (filter-side (aref encoded 1))))
    ;; Decode and scale for unity gain two-way transcoding.
    (scale 0.5 (transcode mid side))))


(cond
  ;;; Test for easily verified user errors
  ((soundp *track*)
    "Error.\nStereo track required.")
  ; Max filter frequency depends on sample rate.
  ((< *sound-srate* 22050)
    "Error.\nTrack sample rate must be at least 22050")
  ((and (= type-mid 5)
        (= type-side 5))
    "Error.\nBoth filters are set to 'None' (bypass).")
  ((and (= type-mid 0) (= gain-mid 0)
        (= type-side 0)(= gain-side 0))
    "Error.\nBoth Parametric filter gains set to 0 (no effect).")
  ((and (= type-side 5)
        (= type-mid 0)
        (= gain-mid 0))
    "Error.\nMid channel parametric filter gain set to 0 (no effect)~%~
    and Side channel filter set to 'None'.")
  ((and (= type-mid 5)
        (= type-side 0)
        (= gain-side 0))
    "Error.\nMid channel filter set to 'None' and~%~
    Side channel parametric filter gain set to 0 (no effect).")
  ; Process if validation successful
  (t (process *track*)))

and as a downloadable plug-in:
mid-side-eq.ny (2.83 KB)

Good contribution! :slight_smile: I have to read the code a bit to understand coding and transcoding, but beyond that, the code works. When I created the plugin I realized that volume was lost in the encoding and transcoding process, so the channels were multiplied by 0.70 and -0.70. I do not know much English, I speak Spanish but I write with the Google translator. It is equal to a plug-in built into audacity, :slight_smile: it would be necessary to disable the debug button. Thanks for the feedback.

Effectively it works out as:
input * 0.707099999 * 0.707099999
which is equivalent to:
input * 0.49999

The reason that there is 2x gain for the round-trip is:

L = Left
R = Right
M = Mid
S = Side

Encoding LR as MS:
M = L + R
S = L - R

Decoding MS as LR:
L = M + S
R = M - S

So for the round trip, we have:
L = (L + R) + (L - R) = 2L
R = (L + R) - (L - R) = 2R


You did extremely well :slight_smile:


It’s a good idea to leave the debug button enabled for a while until certain that there are no bugs. The debug button can then be disabled by adding the header command:

;debugbutton disabled