Following our discussion on the subject, I have a more complete panning-law
patch ready for inspection.  It adds a feature to Rosegarden that permits the
selection of a panning law.  Access is through a submenu of the Audio-Mixer
Settings menu.

The chosen panning law is saved and restored to and from, a field in the
"composition" tag of the XML .rg document.  Each document can have its own
panning law.  The default-studio document can also, of course, have its own
panning law.  If no panning law is found when a document is loaded, the default
0dB law that Rosegarden currently uses is applied.  A panning law is global
throughout the studio, affecting both instrument and submaster panners.

I settled on four possibilities:
    0db Law,
   -3dB Law,
   -6dB Law,
    and a variant of the -3dB Law.

Using the default 0dB panning law, the gain of a channel is initially 0dB and
remains at 0dB as the control is moved to the the center.  From there on, the
signal decreases linearly until it is completely attenuated at the opposite
edge.  Since both channels have a gain of 0dB at the center, the net effect is
a 3dB boost in apparent loudness when the control is centered.  This is
Rosegarden's current behavior.

Using the -3dB panning law, a channel's gain begins at 0dB and decreases to
-3dB as the control is centered.  From there on the signal continues to
decrease until it is completely attenuated at the opposite edge.  The combined
power of both channels remains constant throughout the panning range, resulting
in an apparent constant loudness.

Using the -6dB panning law, a channel's gain begins at 0dB and decreases to
-6dB as the control is moved to the center.  From there on the signal continues
to decrease until it is completely attenuated at the opposite edge.  The 3dB
dip in the combined power of both channels when the control is centered will
cause the extremes to sound louder than the center.

The variant -3dB panning law has the same characteristics as the -3dB law
described above, except that a channel's gain begins at +3dB and decreases to
0dB as the control is moved through the center position.  This panning law must
be used with caution, as the increased edge gains could introduce clipping.

Because the same panning law is always applied to both instrument and submaster
panners, a signal that passes through both of them will experience the
cumulative effects of the same law applied twice.  If, for instance, a panning
law tends to make a centered signal 3dB lower than the default setting, the
same signal will be 6dB lower after it has passed through both controls.  For
that reason the variant -3dB law is my personal favorite.  It provides constant
power, and when the control is centered, the gain is the same as that of the
default panning law.

Tim Munro
--- data/rc/mixer.rc    2013-01-23 05:43:56.000000000 -0800
+++ data/rc/mixer.rc    2013-03-12 00:58:31.547264214 -0700
@@ -44,6 +44,13 @@
       <Action name="submasters_4" text="&amp;4 Submasters" checked="false" 
group="submasters" />
       <Action name="submasters_8" text="&amp;8 Submasters" checked="false" 
group="submasters" />
     </Menu>
+    <Menu name="Panning Law">
+      <text>Panning &amp;Law</text>
+      <Action name="panlaw_0" text="&amp;0dB Law" checked="true" 
group="pan_laws" />
+      <Action name="panlaw_1" text="-&amp;3dB Law" checked="false" 
group="pan_laws" />
+      <Action name="panlaw_2" text="-&amp;6dB Law" checked="false" 
group="pan_laws" />
+      <Action name="panlaw_3" text="-3dB Law (&amp;Louder Variant)" 
checked="false" group="pan_laws" />
+    </Menu>
     <!-- JAS "show_midi" does not appear in pulldown menu. -->
     <Action name="show_midi" append="show_merge" />
     <Action name="show_audio_faders" text="Show &amp;Audio Faders" 
checked="true" append="show_merge" />

--- src/gui/studio/AudioMixerWindow.h   2013-01-07 13:43:39.000000000 -0800
+++ src/gui/studio/AudioMixerWindow.h   2013-03-11 14:00:27.153977178 -0700
@@ -101,6 +101,7 @@
 
     void slotSetInputCountFromAction();
     void slotSetSubmasterCountFromAction();
+    void slotSetPanLaw();
 
     void slotToggleFaders();
     void slotToggleSynthFaders();

--- src/gui/studio/AudioMixerWindow.cpp 2013-02-22 03:23:44.000000000 -0800
+++ src/gui/studio/AudioMixerWindow.cpp 2013-03-11 14:00:27.153977178 -0700
@@ -152,11 +152,19 @@
             (QString("submasters_%1").arg(i), 
SLOT(slotSetSubmasterCountFromAction()));
     }
 
+    QAction *pl_action[4];
+    for (int i = 0; i < 4; i++){
+        pl_action[i] = 0;
+    }
+
+    for (int i = 0; i < 4; i++){
+        pl_action[i] = createAction
+                (QString("panlaw_%1").arg(i), SLOT(slotSetPanLaw()));
+    }
+
     createGUI("mixer.rc");
 
-    // The action->setChecked() stuff must be done after createGUI("mixer.rc"),
-    // if the initial "Number of stereo Inputs" and "Number of Submasters"
-    // menus are to reflect reality.
+    // The action->setChecked() stuff must be done after createGUI("mixer.rc").
     for (int i = 1; i <= 16; i *= 2) {
         if (i == int(m_studio->getRecordIns().size())) {
             ri_action[i]->setChecked(true);
@@ -169,6 +177,12 @@
         }
     }
 
+    for (int i = 0; i < 4; i++) {
+        if (i == AudioLevel::getPanLaw()) {
+            pl_action[i]->setChecked(true);
+        }
+    }
+
     setRewFFwdToAutoRepeat();
 
     // We must populate AFTER the actions are created, or else all the
@@ -1465,6 +1479,17 @@
     populate();
 }
 
+void AudioMixerWindow::slotSetPanLaw()
+{
+    const QObject *s = sender();
+    QString name = s->objectName();
+
+    if (name.left(7) == "panlaw_") {
+        int panLaw = name.right(name.length() - 7).toInt();
+        AudioLevel::setPanLaw(panLaw);
+    }
+}
+
 void AudioMixerWindow::FaderRec::setVisible(bool visible)
 {
     if (visible) {

--- src/base/Composition.cpp    2013-01-07 13:43:44.000000000 -0800
+++ src/base/Composition.cpp    2013-03-11 14:00:27.154977178 -0700
@@ -22,6 +22,7 @@
 #include "base/Profiler.h"
 #include "BasicQuantizer.h"
 #include "NotationQuantizer.h"
+#include "base/AudioLevel.h"
 
 #include <iostream>
 #include <iomanip>
@@ -1885,6 +1886,10 @@
     composition << "\" playmetronome=\"" << m_playMetronome;
     composition << "\" recordmetronome=\"" << m_recordMetronome;
     composition << "\" nexttriggerid=\"" << m_nextTriggerSegmentId;
+
+    // Place the number of the current pan law in the composition tag.
+    int panLaw = AudioLevel::getPanLaw();
+    composition << "\" panlaw=\"" << panLaw;
     composition << "\">" << endl << endl;
 
     composition << endl;

--- src/document/RoseXmlHandler.cpp     2013-01-19 06:59:49.000000000 -0800
+++ src/document/RoseXmlHandler.cpp     2013-03-11 14:00:27.154977178 -0700
@@ -703,6 +703,15 @@
             getComposition().setEndMarker(endMarkerStr.toInt());
         }
 
+        QString panLawStr = atts.value("panlaw");
+        if (!panLawStr.isEmpty()) {
+            int panLaw = panLawStr.toInt();
+            AudioLevel::setPanLaw(panLaw);
+        } else {
+            // Since no "panlaw" was found in this tag, apply the default.
+            AudioLevel::setPanLaw(0);
+        }
+
     } else if (lcName == "track") {
 
         if (m_section != InComposition) {

--- src/base/AudioLevel.h       2013-01-07 13:43:44.000000000 -0800
+++ src/base/AudioLevel.h       2013-03-12 03:56:21.727172807 -0700
@@ -52,6 +52,18 @@
     // fast if "levels" doesn't change often -- for audio segment previews
     static int   multiplier_to_preview(float multiplier, int levels);
     static float preview_to_multiplier(int level, int levels);
+
+    // Set or retrieve the number of the pan law.
+    static void setPanLaw(int panLaw) { m_panLaw = panLaw; }
+    static int getPanLaw() { return m_panLaw; }
+
+    // Apply pan law
+    static float panGainLeft(float pan);
+    static float panGainRight(float pan);
+
+private:
+
+    static int m_panLaw;  // number of pan law currently in use
 };

 }

--- src/base/AudioLevel.cpp     2013-01-07 13:43:44.000000000 -0800
+++ src/base/AudioLevel.cpp     2013-03-12 02:44:40.927034770 -0700
@@ -260,6 +260,76 @@
     const LevelList &ll = getPreviewLevelCache(levels);
     return ll[level];
 }
-       
+
+int AudioLevel::m_panLaw = 0;
+
+float
+AudioLevel::panGainLeft(float pan)  // Apply panning law to left channel
+{
+    if (m_panLaw == 3) {
+        // -3dB Panning Law (variant)
+        //
+        // This law has the same characteristics as the -3dB law described
+        // below, except that a channel's gain begins at +3dB and decreases to
+        // 0dB as the control is moved through the center position.  This
+        // setting must be used with caution, as the increased edge gains could
+        // introduce clipping.
+        //
+        return sqrtf(fabsf((100.0 - pan) / 100.0));  // -3dB pan law  (variant)
+
+    } else if (m_panLaw == 2) {
+        // -6dB Panning Law
+        //
+        // A channel's gain begins at 0dB and decreases to -6dB as the control
+        // is moved to the center.  From there on the signal continues to
+        // decrease until it is completely attenuated at the opposite edge.
+        // The 3dB dip in the combined power of both channels when the control
+        // is centered will cause the extremes to sound louder than the center.
+        //
+        return (100.0 - pan) / 200.0;
+
+    } else if (m_panLaw == 1) {
+        // -3dB Panning Law
+        //
+        // A channel's gain begins at 0dB and decreases to -3dB as the control
+        // is centered.  From there on the signal continues to decrease until
+        // it is completely attenuated at the opposite edge.  The combined
+        // power of both channels remains constant throughout the panning
+        // range, resulting in an apparent constant loudness.
+        //
+        return sqrtf(fabsf((100.0 - pan) / 200.0));
+
+    } else {
+        // OdB Panning Law (default)
+        //
+        // A channel's gain begins at 0dB and remains at 0dB as the control is
+        // moved to the the center.  From there on the signal decreases
+        // linearly till it is completely attenuated at the opposite edge.
+        // Since both channels have a gain of 0dB at the center, the net
+        // effect is a 3dB boost in apparent loudness when the control is
+        // centered.
+        //
+        return (pan > 0.0) ? (100.0 - pan) / 100.0 : 1.0;
+
+    }
+}
+
+float
+AudioLevel::panGainRight(float pan)  // Apply panning law to right channel
+{
+    if (m_panLaw == 3) {
+        return sqrtf(fabsf((100.0 + pan) / 100.0));  // -3dB pannig law 
(variant)
+
+    } else if (m_panLaw == 2) {
+        return (100.0 + pan) / 200.0;  // -6dB pan law
+
+    } else if (m_panLaw == 1) {
+        return sqrtf(fabsf((100.0 + pan) / 200.0));  // -3dB panning law
+
+    } else {
+        return (pan < 0.0) ? (100.0 + pan) / 100.0 : 1.0;  // 0dB panning law 
(default)
+
+    }
+}
 
 }

--- src/sound/AudioProcess.cpp  2013-01-07 13:43:43.000000000 -0800
+++ src/sound/AudioProcess.cpp  2013-03-11 14:00:27.155977178 -0700
@@ -478,8 +478,10 @@
 
     float volume = AudioLevel::dB_to_multiplier(dB);
 
-    rec.gainLeft = volume * ((pan > 0.0) ? (1.0 - (pan / 100.0)) : 1.0);
-    rec.gainRight = volume * ((pan < 0.0) ? ((pan + 100.0) / 100.0) : 1.0);
+//  rec.gainLeft = volume * ((pan > 0.0) ? (1.0 - (pan / 100.0)) : 1.0);
+//  rec.gainRight = volume * ((pan < 0.0) ? ((pan + 100.0) / 100.0) : 1.0);
+    rec.gainLeft = volume * AudioLevel::panGainLeft(pan);
+    rec.gainRight = volume * AudioLevel::panGainRight(pan);
 }
 
 void
@@ -1541,8 +1543,10 @@
 
     float volume = AudioLevel::dB_to_multiplier(dB);
 
-    rec.gainLeft = volume * ((pan > 0.0) ? (1.0 - (pan / 100.0)) : 1.0);
-    rec.gainRight = volume * ((pan < 0.0) ? ((pan + 100.0) / 100.0) : 1.0);
+//  rec.gainLeft = volume * ((pan > 0.0) ? (1.0 - (pan / 100.0)) : 1.0);
+//  rec.gainRight = volume * ((pan < 0.0) ? ((pan + 100.0) / 100.0) : 1.0);
+    rec.gainLeft = volume * AudioLevel::panGainLeft(pan);
+    rec.gainRight = volume * AudioLevel::panGainRight(pan);
     rec.volume = volume;
 }
 
------------------------------------------------------------------------------
Symantec Endpoint Protection 12 positioned as A LEADER in The Forrester  
Wave(TM): Endpoint Security, Q1 2013 and "remains a good choice" in the  
endpoint security space. For insight on selecting the right partner to 
tackle endpoint security challenges, access the full report. 
http://p.sf.net/sfu/symantec-dev2dev
_______________________________________________
Rosegarden-devel mailing list
[email protected] - use the link below to unsubscribe
https://lists.sourceforge.net/lists/listinfo/rosegarden-devel

Reply via email to