Old 02-23-2016, 09:34 PM   #1
CoreyScogin
Human being with feelings
 
CoreyScogin's Avatar
 
Join Date: Dec 2013
Posts: 9
Default JS AutoMixer: Has this been done before?

Hello everyone. This is my first post on the Reaper forums but I've been a user for 4+ years. Greetings to you all.

I have recently been editing a weekly podcast. For one episode, the host set up 2 microphones and conducted a round-table discussion with 5 or so people. This caused some issues with my typical 1 person per mic workflow. Primarily, since the volume of the person speaking was very inconsistent throughout the recording, it was difficult to set gates in such a way that attenuated the mic not in use without chopping the audio of the quieter participants. The same difficulty would be had with a downward expander.

This prompted me to consider using a Dugan style automixer to attenuate the weaker channel(s) without using a fixed threshold. I heard they work quite well for multiple channels of dialog. A couple days of searching yielded no offline plugin solution. The Waves plugin for SoundGrid was the only thing I could find and it's real-time only.

Since I could not find an AutoMixer plugin, I created one in JSFX. I am a software developer by trade but I was completely unfamiliar with programming DSP and the syntax of JSFX so there may be plenty of errors in this code. Some of the code was borrowed from other plugins or snippets posted elsewhere (thanks).

Usage: Drop this plugin on every track that you want to include in the auto-mix group. The plugin will attenuate itself according to the auto-mixing calculation (attenuation = Total input level of all channels - input level of this channel).

If any of you find this plugin useful or are interested in reviewing the code, I welcome your comments / improvements. I have commented where there are things I think could be improved or wasn't sure of something. The plugin functions as expected but there may be glaring mistakes that I am not seeing in the code itself.

Sorry for the long post.
Code:
desc:AutoMixer
author: Corey Scogin
version: 1.1
tags: volume, attenuation, automation
donation: BTC 3JSqBVB1wQeyGHP8ktHuefQMzPdHKhLYQY
  ETH 0x1a753E71D4C9473c2FEb1946CBc6b32CE853c189
about: 
  # AutoMixer
  ## Description
  Based on the gain sharing automatic microphone mixing algorithm created by [Dan Dugan](https://www.dandugan.com), 
  this plugin attenuates channels that are not in use helping to smoothly cut background noise and bleed.
  Useful for spoken word content. 
  ## Instructions
  Drop this plugin on each channel in the group. 
  - Inactive channels will be attenuated helping to decrease background noise.
  - Multiple active channels will be attenuated according to the sum of all channels helping to keep the overall volume under control.
  See [Wikipedia](https://en.wikipedia.org/wiki/Automixer) for a more thorough description.
changelog:
2016-02-26 | Corey Scogin 
  -Removed the linear to dB conversions. All attenuation values calculated directly now.
  -Clear the buffer when the window changes. Keeping old buffer values would result in unexpected behavior.
  -Added the freembuf call to indicate how much memory is used.

// Known Bugs:
//  -If an instance of a plugin is removed or bypassed, the others do not know and it throws off the mix calculations.
//  You may have to close and reopen the project in order to reset the instance count and restore functionality.
//  Is there a better way to keep track of the number of plugin instances?

options:gmem=AutoMixer

slider2:300<1,10000,10>RMS Window Length (ms)
slider3:0<0,1,1{Rectangular,Efficient}>RMS Window Type

@init
 
  minRmsValue = 10^(-144/20) ; // Minimum RMS value (-144) in linear representation
  
  // Options      
  ext_noinit = 1;   // Do not call @init on play else the instance ID gets reset.    
  
  // Variables/Properties
  gmem[0] += 1;     // Create an id for each instance. 
                    // Is there a better way to determine how many instances are running?
  thisID = gmem[0]; // note local ID for debug display. 
                    // Global Memory storage descriptions:
                    //  gmem[0] stores how many instances of this plugin exist
                    //  gmem[n] stores the current rms value for each of the n instances of the plugin

@slider 
  // Recalculate constants
  
  // Sample window
  window = slider2 * .001 * srate; // number of samples in selected time frame (ms)
  
  // Constants for efficient moving average approximation
  b1 = exp(-1 / window); 
  a0 = 1 - b1; 
  
  // reset persisted values to ensure new calculations are clean
  buf = 0;                  // Reset buffer position in case the window gets smaller
  memset( buf, 0, window ); // Clear the entire new buffer memory space
  rmsSum = 0;               // Clear the rmsSum because old buffer values are no longer valid

  freembuf(window + 1);     // Tell the memory manager how much memory we need or don't need

@sample
  
  // Average all channels in this track (typically 2)
  i=0;
  trkSum=0;
  loop(num_ch,                // For each channel
        trkSum += spl(i);     // Add the sample values
        i += 1;
       ); 
  trkAvg = trkSum / num_ch;   // Calculate the average
  
  
  // Calculate the new rms value
  slider3 == 0 ? (      // Window Type == Rectangular
                        // Less efficient true rectangular window moving average 
    splSq = trkAvg ^ 2; // Sample (or average of channel samples) squared for moving average sum.
    rmsSum -= buf[0];   // Subtract the oldest value square from the sum
    rmsSum += splSq;    // add the newest value square to the sum
    buf[0] = splSq;     // add the newest value square to the buffer
  
    (buf+=1) >= window ? buf=0;   // increment buffer position
  
    rms = sqrt( rmsSum / window ) ; // Calculate the new RMS value
    
  ):( // Window Type == efficient approximation 
      // Efficient moving average approximation
    fout = a0 * (trkAvg ^ 2) + b1 * fout;
    rms = sqrt(fout);
  );
  
  // Store current RMS value in shared memory for comparison by other instances of this plugin
  gmem[thisID] = rms;
  
  // Calculate total RMS level of all plugin instances
  i = 0;
  sum = 0;
  loop(gmem[0], 
           i += 1;
           sum += gmem[i];
       );     

  // Put a minimum limit on the RMS value...effectively zero
  rms < minSplValue ? rms = minSplValue; // -144 dB
  
  // The AutoMixer Algorithm
  attSpl = rms / sum; // For reference: subtraction in dB corresponds to division in linear
    
  // Attenuate each channel sample value
  i = 0;
  loop(num_ch,
        spl(i) = spl(i) * attspl;
        i += 1;
        );       

// Possible future modifications:
//    -Instance grouping
//    -Better ID management
** Updates:
2016-02-24
- Changed all of the calculations to use linear values instead of logarithmic ones. This eliminates a number of linear to log conversions.
- The buffer is now cleared when the window changes. There was previously some unexpected behavior as invalid samples were removed from the sum-of-squares value after the window changed size.

Last edited by CoreyScogin; 06-11-2018 at 09:00 PM. Reason: updated code
CoreyScogin is offline   Reply With Quote
Old 02-23-2016, 10:07 PM   #2
EpicSounds
Human being with feelings
 
EpicSounds's Avatar
 
Join Date: Jul 2009
Posts: 5,095
Default

very interesting. Thanks for sharing.
__________________
REAPER Video Tutorials, Tips & Tricks and more at The REAPER Blog
EpicSounds is offline   Reply With Quote
Old 02-24-2016, 05:35 AM   #3
X-Raym
Human being with feelings
 
X-Raym's Avatar
 
Join Date: Apr 2013
Location: France
Posts: 5,214
Default

Thanks for sharing :P

Maybe can you make a video de mo of it ?
X-Raym is offline   Reply With Quote
Old 02-24-2016, 05:40 AM   #4
heda
Human being with feelings
 
heda's Avatar
 
Join Date: Jun 2012
Location: Spain
Posts: 4,795
Default

what do you want? Leave all mixer engineers jobless?
hehe.. just joking

Very welcome. Good first post. Smart. Will try it later.
__________________
HeDaScripts for REAPER | VIP Donations
heda is offline   Reply With Quote
Old 02-24-2016, 07:43 AM   #5
cyrano
Human being with feelings
 
cyrano's Avatar
 
Join Date: Jun 2011
Location: Belgium
Posts: 3,239
Default

Thanks!

I have already installed your plugin and will try it on the first occasion. That might take a while, as I don't often record round table conversations.
__________________
“Political correctness is fascism pretending to be manners.” George Carlin
cyrano is offline   Reply With Quote
Old 02-24-2016, 01:59 PM   #6
CoreyScogin
Human being with feelings
 
CoreyScogin's Avatar
 
Join Date: Dec 2013
Posts: 9
Default

Quote:
Originally Posted by X-Raym View Post
Thanks for sharing :P

Maybe can you make a video de mo of it ?
I may. I'd like to spend a little more time debugging it before putting the time into a video demo.
CoreyScogin is offline   Reply With Quote
Old 02-24-2016, 02:07 PM   #7
CoreyScogin
Human being with feelings
 
CoreyScogin's Avatar
 
Join Date: Dec 2013
Posts: 9
Default

Quote:
Originally Posted by cyrano View Post
Thanks!

I have already installed your plugin and will try it on the first occasion. That might take a while, as I don't often record round table conversations.
It's definitely a niche plugin. I don't think it will work well for music at all. The whole purpose is to avoid spending hours automating a multi-mic dialog recording. This plus some volume leveling after it should get pretty close to a finished edit and be less prone to mistakes than using gates on each channel.
CoreyScogin is offline   Reply With Quote
Old 02-24-2016, 02:16 PM   #8
CoreyScogin
Human being with feelings
 
CoreyScogin's Avatar
 
Join Date: Dec 2013
Posts: 9
Default

I made a couple of edits and updated the original post code section.

My math skills are both limited and rusty. I forgot that a subtraction of log values is a division of linear values. This was what I needed to remove a number of linear sample value to logarithmic dB value conversions. It's slightly more efficient now.

There was an error with how the buffer worked when the window was changed. Clearing the buffer when the window changes seems to have fixed that. I would not recommend automating a change in the window since the buffer gets cleared and messes up the calculation until it fills up again. I don't think there's a simple solution to adjusting the window on the fly with accurate results nor do I think it's needed. Typical use case would not require a window change on the fly.

Again, I'd love any suggestions or corrections/improvements on the code if any of you are interested in digging through it.
CoreyScogin is offline   Reply With Quote
Old 02-24-2016, 08:27 PM   #9
EpicSounds
Human being with feelings
 
EpicSounds's Avatar
 
Join Date: Jul 2009
Posts: 5,095
Default

I wish I had this a couple years ago when I was editing podcasts weekly with 3 or more people talking.
__________________
REAPER Video Tutorials, Tips & Tricks and more at The REAPER Blog
EpicSounds is offline   Reply With Quote
Old 02-25-2016, 12:28 AM   #10
EpicSounds
Human being with feelings
 
EpicSounds's Avatar
 
Join Date: Jul 2009
Posts: 5,095
Default

ok had a quick play with it this evening on some old podcast files.

First I normalized each file to -23LUFS
Then I inserted your Automixer plugin on each track and found that a window of 300 works about right.

Maybe this setting would depend on each person's voice? Would you use 100ms for one person and 500 for another?

Result was a pretty good sounding podcast, or at least a much better starting point than I would have expected. Much cleaner sound than without and managed levels well when everyone is laughing.

Still need a limiter on the master and would probably still use some NR, comp, EQ and DeEssing on the individual tracks.

Seems to work well.
__________________
REAPER Video Tutorials, Tips & Tricks and more at The REAPER Blog
EpicSounds is offline   Reply With Quote
Old 02-25-2016, 09:09 AM   #11
CoreyScogin
Human being with feelings
 
CoreyScogin's Avatar
 
Join Date: Dec 2013
Posts: 9
Default

Quote:
Originally Posted by EpicSounds View Post
First I normalized each file to -23LUFS
Then I inserted your Automixer plugin on each track and found that a window of 300 works about right.

Maybe this setting would depend on each person's voice? Would you use 100ms for one person and 500 for another?

Result was a pretty good sounding podcast, or at least a much better starting point than I would have expected. Much cleaner sound than without and managed levels well when everyone is laughing.

Still need a limiter on the master and would probably still use some NR, comp, EQ and DeEssing on the individual tracks.
Great! I'm glad to have someone else use it and find it useful.

300ms may be a better default window. When testing I used a range of windows and didn't consciously settle on 500. That was just my initial guess.

I also tested this a bit more last night with a couple of podcasts. My plugin chain ended up very similar to what you suggest. A high pass filter, AutoMixer, DeEsser, Compressor, then a volume leveler (Vocal Rider) on each track and a limiter on the main bus.

When testing with a recent podcast, one issue that I found was in short sections of silence. One of the mic channels had significant ambient noise (recorded in a different location) so since neither were being used for a moment, the AutoMixer didn't keep that noisy channel attenuated and may even favor the noisy one (by design). In that case, a gate or downward expander would be needed to keep the ambient noise out.

I've been pleasantly surprised with how well this works especially in a multi-mic recording.
CoreyScogin is offline   Reply With Quote
Old 07-11-2016, 02:57 AM   #12
Lakesidesound
Human being with feelings
 
Join Date: May 2016
Posts: 2
Default

Hey Corey! It's a great tool - have you ever thought of making a vst version so we can use it in other DAWs? If not yourself, maybe some developer from KVR will do it.
Lakesidesound is offline   Reply With Quote
Old 07-11-2016, 03:17 AM   #13
X-Raym
Human being with feelings
 
X-Raym's Avatar
 
Join Date: Apr 2013
Location: France
Posts: 5,214
Default

@Lakesidesound
JS effects can be imported in other DAW thanks to ReaJS VST in ReaPlug
REAPER | ReaPlugs :P
X-Raym is offline   Reply With Quote
Old 07-11-2016, 09:04 PM   #14
CoreyScogin
Human being with feelings
 
CoreyScogin's Avatar
 
Join Date: Dec 2013
Posts: 9
Default

Quote:
Originally Posted by Lakesidesound View Post
Hey Corey! It's a great tool - have you ever thought of making a vst version so we can use it in other DAWs? If not yourself, maybe some developer from KVR will do it.
I haven't. While I am a software developer, developing a VST plugin isn't something I've looked into.

The bug I mention in the JS comments header means that while this plugin is handy, it's not ready for prime-time so I'm not sure a VST would be currently worth the trouble.
CoreyScogin is offline   Reply With Quote
Old 07-12-2016, 08:08 AM   #15
Jason Brian Merrill
Human being with feelings
 
Jason Brian Merrill's Avatar
 
Join Date: Jun 2006
Location: Northeastern PA, USA
Posts: 20,667
Default

this would have been super helpful when I had a lot of jobs that had dialogue. I am sure it will be helpful again, thanks!
__________________
Beliefs do not require respect. People do.
Jason Brian Merrill is online now   Reply With Quote
Old 07-14-2016, 11:53 PM   #16
Lakesidesound
Human being with feelings
 
Join Date: May 2016
Posts: 2
Default

X-Raym - thanks for the ReaPlugs link! Got the automixer working on Cubase, however it crashes Cubase on some occations (when duplicating the plugin to another channel), so gotta be careful there.
Lakesidesound is offline   Reply With Quote
Old 07-20-2016, 04:49 PM   #17
junh1024
Human being with feelings
 
Join Date: Feb 2014
Posts: 110
Default

http://www.waves.com/plugins/vocal-r...to-vocal-rider

This is available for almost-realtime on PC.

I think what you're trying to make is a leveller, is that right?

Don't think you need to link every instance, just set the same target.
junh1024 is offline   Reply With Quote
Old 07-20-2016, 07:37 PM   #18
CoreyScogin
Human being with feelings
 
CoreyScogin's Avatar
 
Join Date: Dec 2013
Posts: 9
Default

Quote:
Originally Posted by junh1024 View Post
http://www.waves.com/plugins/vocal-r...to-vocal-rider

This is available for almost-realtime on PC.

I think what you're trying to make is a leveller, is that right?

Don't think you need to link every instance, just set the same target.
Not exacly. An AutoMixer is more like a multi-input leveler or multi-input cross-downward-expander. Every input contributes to the leveling algorithm.

https://en.wikipedia.org/wiki/Automixer
http://www.waves.com/plugins/dugan-automixer
CoreyScogin is offline   Reply With Quote
Old 07-22-2016, 08:30 PM   #19
junh1024
Human being with feelings
 
Join Date: Feb 2014
Posts: 110
Default

Ok whoops, my bad, so more like an auto-ducker for quiet things ispz
junh1024 is offline   Reply With Quote
Old 05-21-2018, 03:10 PM   #20
Dougie
Human being with feelings
 
Join Date: May 2018
Posts: 1
Default

Really useful idea, I'm current'y editing the occasional podcast so this could come in very handy. Can anyone point a newbie in the right direction to get this working?
I'm assuming I save the script as a text file somewhere?
Dougie is offline   Reply With Quote
Old 05-21-2018, 03:20 PM   #21
EpicSounds
Human being with feelings
 
EpicSounds's Avatar
 
Join Date: Jul 2009
Posts: 5,095
Default

Quote:
Originally Posted by Dougie View Post
Really useful idea, I'm current'y editing the occasional podcast so this could come in very handy. Can anyone point a newbie in the right direction to get this working?
I'm assuming I save the script as a text file somewhere?
save it to reaper's data/effects folder as a plain text file with the extension ".jsfx"
__________________
REAPER Video Tutorials, Tips & Tricks and more at The REAPER Blog
EpicSounds is offline   Reply With Quote
Old 05-21-2018, 09:29 PM   #22
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Germany
Posts: 6,115
Default

Maybe somebody can include it in Reapack ?!?!?

-Michael
__________________
www.boa-sorte.de
mschnell is offline   Reply With Quote
Old 05-21-2018, 09:36 PM   #23
JamesPeters
Human being with feelings
 
JamesPeters's Avatar
 
Join Date: Aug 2011
Location: Near a big lake
Posts: 1,943
Default

Quote:
Originally Posted by mschnell View Post
Maybe somebody can include it in Reapack ?!?!?

-Michael
If the author doesn't want to maintain the plugin (and a repository), what do you suggest? Are you hoping someone else will take over his work and also maintain it? It has 2 things in "known bugs" already, and the author hasn't been back since he posted this.
__________________
http://petersamplification.com
Core i3-6300 - MSI B150M Mortar w/ Intel HD530 - 8 GB RAM - Asus Xonar DX - MX Linux (MX-17.1_x64) - REAPER for Linux
JamesPeters is offline   Reply With Quote
Old 05-22-2018, 08:46 AM   #24
EpicSounds
Human being with feelings
 
EpicSounds's Avatar
 
Join Date: Jul 2009
Posts: 5,095
Default

Quote:
Originally Posted by JamesPeters View Post
If the author doesn't want to maintain the plugin (and a repository), what do you suggest? Are you hoping someone else will take over his work and also maintain it? It has 2 things in "known bugs" already, and the author hasn't been back since he posted this.
he's active in my facebook group. With his permission it could go into the ReaTeam repo
__________________
REAPER Video Tutorials, Tips & Tricks and more at The REAPER Blog
EpicSounds is offline   Reply With Quote
Old 05-22-2018, 05:28 PM   #25
JamesPeters
Human being with feelings
 
JamesPeters's Avatar
 
Join Date: Aug 2011
Location: Near a big lake
Posts: 1,943
Default

Quote:
Originally Posted by EpicSounds View Post
he's active in my facebook group. With his permission it could go into the ReaTeam repo
Well if he's also willing to maintain it, that would be really nice.
__________________
http://petersamplification.com
Core i3-6300 - MSI B150M Mortar w/ Intel HD530 - 8 GB RAM - Asus Xonar DX - MX Linux (MX-17.1_x64) - REAPER for Linux
JamesPeters is offline   Reply With Quote
Old 05-22-2018, 09:56 PM   #26
CoreyScogin
Human being with feelings
 
CoreyScogin's Avatar
 
Join Date: Dec 2013
Posts: 9
Default

Quote:
Originally Posted by EpicSounds View Post
he's active in my facebook group. With his permission it could go into the ReaTeam repo
I certainly don't mind it being included in the ReaTeam repo if that's the best route. I also don't mind creating my own ReaPack repo if that's preferred. I'll just need a little time to review those requirements.

I've used the plugin often over the past two years without issue. There is the one bug I mentioned but in my use it has rarely caused problems.

I'm happy to maintain the plugun as-is, meaning, fix new compatibility issues if they were to arise, however, I can't commit to ironing out all the kinks that people may run into in edge cases nor can I commit to adding more features. This is pretty far down my priority list right now given that it works, however, it is still on the list and so may get some attention. I'm also happy for someone to submit edits. I'm sure many of you have plenty of JSFX wisdom you could could impart to this plugin.

@Jon, @Michael, let me know whether you think the better option would be to include it in the ReaTeam repo or in its own ReaPack repo. If the former, I'll follow your contribution instructions and submit a pull request. Otherwise, I can get its existing GitHub repo configured for ReaPack.
CoreyScogin is offline   Reply With Quote
Old 05-23-2018, 06:48 AM   #27
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Germany
Posts: 6,115
Default

I suppose doing your own repository makes sense if you want to publish a bunch of plugins.

If you want to use the ReaTeam repository you should contact cfillion about it.

Thanks !
-Michael
__________________
www.boa-sorte.de
mschnell is offline   Reply With Quote
Old 05-23-2018, 07:49 AM   #28
EpicSounds
Human being with feelings
 
EpicSounds's Avatar
 
Join Date: Jul 2009
Posts: 5,095
Default

you don't need your own repo, the reapack index thing is a bit complicated.

The main thing with scripts or JSFX is getting the header right, description, links to documentation, version numbers, etc.
Just look at a few that are published for the format.

I have a github page, I fork the ReaTeam JSFX folder, add my own files, then send a pull request. They fix my mistakes and put it through.
__________________
REAPER Video Tutorials, Tips & Tricks and more at The REAPER Blog
EpicSounds is offline   Reply With Quote
Old 05-23-2018, 03:24 PM   #29
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Germany
Posts: 6,115
Default

Quote:
Originally Posted by EpicSounds View Post
The main thing with scripts or JSFX is getting the header right, description, links to documentation, version numbers, etc.
Nice stuff: allowing for detailed description ("about") in MarkDown format, also makes the plugin findable in ReaPack for the users.

-Michael
__________________
www.boa-sorte.de
mschnell is offline   Reply With Quote
Old 06-12-2018, 07:36 PM   #30
CoreyScogin
Human being with feelings
 
CoreyScogin's Avatar
 
Join Date: Dec 2013
Posts: 9
Default Now in ReaPack

The plugin is now available in ReaPack via the ReaTeam/JSFX repo.
CoreyScogin is offline   Reply With Quote
Old 06-15-2018, 09:41 AM   #31
Ozman
Human being with feelings
 
Join Date: Feb 2015
Posts: 475
Default

Quote:
Originally Posted by CoreyScogin View Post
The plugin is now available in ReaPack via the ReaTeam/JSFX repo.
Cool.
I'm gonna have to give this one try.
Ozman is offline   Reply With Quote
Old 06-15-2018, 09:47 AM   #32
Ozman
Human being with feelings
 
Join Date: Feb 2015
Posts: 475
Default

This script, while great for dialog, reminds me of a internal question I once had (a few months ago) regarding something similar for music...

The feasibility of a script that automatically premixes chosen tracks along a chosen curve (e.g. pink noise or 4.5db slope noise). I came across youtube videos of folks using pink noise start their mixes. And I was wondering if this could be automatically done with chosen tracks. (I guess it would compare their volumes to the noise using FFT data or something).
Ozman is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 08:35 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2018, vBulletin Solutions Inc.