Hello,

Last saturday in a drum-and-bass party I saw how TC (ab)used the effect
of looping and then shortening the loop until one is actually building a
whole new sound out of it that sounds somewhat like a square wave. I
found it easy to implement on the controller mapping side and given that
the Xponent has a whole bunch of FX buttons that are unused in Mixxx
(they are waiting for the very acclaimed Ladspa support, or just more
effects :D) I used them to implement this. I attach the bzr patch.

Now the fourth FX knob and button on each deck can be used as follows:

- The button enables/disables looping with BPM-determined loop size and
in point at current sample.

- The knob controls the loop size, the knob range is divided in 10
logarithmic steps (coincident with the points around the knob) that set
the loop size from 2^-6 to 2^4 beats.

- The third FX button can be used too. It behaves similar to the other
button but instead it uses the BPM of the *other* deck. This can be used
in fun transitions, specially when going to higher BPM songs (for
example, you start with a dubstep song and then start to roll a loop out
of it based on its own BPM and shorten it, then when it is almost as
short as one beat you click the other button so it gets synced at, lets
say 120 BPM, then you cue in a techno track and suddenly the looping
dubstep can be perceived as a cool minimal glitch of the techno track).

When developing this I found that it would be useful to show on the
screen the current multiple of the beat size used for the looping.
However there is no way to do such thing with current Mixxx. I thought
that, because revamping the whole GUI system to allow more flexible
mapping based controls is just too hard, maybe just a "user-determined
text control" could be added (actually most skins have some blank space
next to the bpm and song name indicators where we could place this
control) that MIDI scripts can find useful to provide extra information.

On the other hand, I tried to fix the issues with by 'round-bpm' [1]
branch. It is updated to trunk, the leaking merge conflict tokens have
been removed, and the bug involving the configuration not being properly
loaded has been fixed. I feel that it is ready to enter trunk and get
into the next 1.10 release. Note that while fixing the config issue, I
found out that the problem was that the config classes do not handle
floats properly. There was a general mood in IRC that those classes are
a mess and should not be touched so I just used and integer instead, but
probably someone familiar with that code wants to either fix the floats
support or just remove the toFloat() and related methods to avoid people
being tempted to store floats there and end up spending endless hours
debugging (:

Cheers,

JP

[1] https://code.launchpad.net/~raskolnikov/mixxx/round-bpm/+merge/42349
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: [email protected]
# target_branch: lp:mixxx
# testament_sha1: 1de8720b8be26a366b2ea3c858fa1d49cdf48ebe
# timestamp: 2011-04-12 12:34:23 +0200
# source_branch: lp:mixxx
# base_revision_id: [email protected]
# 
# Begin patch
=== modified file 'mixxx/res/midi/M-Audio-Xponent-scripts.js'
--- mixxx/res/midi/M-Audio-Xponent-scripts.js	2010-12-24 16:19:41 +0000
+++ mixxx/res/midi/M-Audio-Xponent-scripts.js	2011-04-12 10:32:50 +0000
@@ -66,6 +66,10 @@
     27: 5
 };
 MaudioXponent.timer = [-1, -1];  // Temporary storage of timer IDs
+
+MaudioXponent.syncloop_min = -6.;
+MaudioXponent.syncloop_max = 4.;
+
 MaudioXponent.state = { 
     "flashes" : 0, 
     "faderpos" : 0, 
@@ -77,6 +81,10 @@
     "plnumberneg" : 0,
     "scratching1" : 0,
     "scratching2" : 0,
+    "syncloop1" : 0,
+    "syncloop2" : 0,
+    "syncloopval1" : 128,
+    "syncloopval2" : 128
 }; // Temporary state variables
 
 // ----------   Functions    ----------
@@ -128,6 +136,8 @@
     engine.connectControl("[Channel2]","hotcue_4_enabled","MaudioXponent.hotcue");
     engine.connectControl("[Channel2]","hotcue_5_enabled","MaudioXponent.hotcue");
 
+    engine.connectControl("[Channel1]", "track_samples", "MaudioXponent.onTrackChange");  engine.connectControl("[Channel2]", "track_samples", "MaudioXponent.onTrackChange");
+
     engine.connectControl("[Channel1]", "loop_enabled", "MaudioXponent.onLoopExit");
     engine.connectControl("[Channel1]", "loop_start_position", "MaudioXponent.onLoopIn");
     engine.connectControl("[Channel1]", "loop_end_position", "MaudioXponent.onLoopOut");
@@ -335,8 +345,7 @@
 MaudioXponent.onLoopExit = function(channel, control, value, status) 
 {
     channelnow = control == "[Channel2]" ? 1 : 0;
-    midi.sendShortMsg(MaudioXponent.on + channelnow, 0x2A, 
-		      engine.getValue (control, value) == 1);
+    midi.sendShortMsg(MaudioXponent.on + channelnow, 0x2A, value);
 }
 
 MaudioXponent.loopin = function(channel, control, value, status) {
@@ -434,6 +443,85 @@
     }
 };
 
+MaudioXponent.onTrackChange = function(channel, control, value, status) 
+{
+    channelnow = control == "[Channel2]" ? 1 : 0;
+    var currentdeck = channelnow+1;
+    MaudioXponent.state["syncloop" + currentdeck] = 0;
+    midi.sendShortMsg (MaudioXponent.on + channelnow, 15, 0x00);
+    midi.sendShortMsg (MaudioXponent.on + channelnow, 14, 0x00);
+}
+
+MaudioXponent.syncloopOther = function(channel, control, value, status) 
+{
+    MaudioXponent.doSyncloop (channel, control, value, status, 1);
+}
+
+MaudioXponent.syncloop = function(channel, control, value, status) 
+{
+    MaudioXponent.doSyncloop (channel, control, value, status, 0);
+}
+
+MaudioXponent.doSyncloop = function(channel, control, value, status, other) 
+{
+    //script.debug(channel, control, value, status);
+    var currentdeck = channel+1;
+    var chctl = "[Channel"+currentdeck+"]";
+    var otherchctl = "[Channel"+(!channel+1)+"]";
+
+    var bpm = engine.getValue (other?otherchctl:chctl, "bpm");
+    if (MaudioXponent.state["syncloop"+currentdeck] != 1+other && bpm)
+    {
+	var pos   = engine.getValue (chctl, "playposition");
+	var total = engine.getValue (chctl, "track_samples");
+	var begin = pos * total;
+	
+	engine.setValue (chctl, "loop_start_position", begin);
+	midi.sendShortMsg (MaudioXponent.on + channel, control, 0x01);
+	midi.sendShortMsg (MaudioXponent.on + channel, control-1 + other*2, 0x00);
+	MaudioXponent.syncloopSet (channel, control, value, status, begin, other);
+	if (!engine.getValue (chctl, "loop_enabled"))
+	    engine.setValue (chctl, "reloop_exit", 1);
+	MaudioXponent.state["syncloop"+currentdeck] = 1 + other;
+    }
+    else
+    {
+	engine.setValue (chctl, "reloop_exit", 1);
+	midi.sendShortMsg (MaudioXponent.on + channel, control, 0x00);
+	MaudioXponent.state["syncloop"+currentdeck] = 0;
+    }
+};
+
+MaudioXponent.syncloopSet = function(channel, control, value, status, begin, other)
+{
+    var currentdeck = channel+1;
+    var chctl = "[Channel"+currentdeck+"]";
+    var otherchctl = "[Channel"+(!channel+1)+"]";
+    var loopsize = MaudioXponent.state["syncloopval"+currentdeck];
+    
+    var min = MaudioXponent.syncloop_min;
+    var max = MaudioXponent.syncloop_max;
+    loopsize = Math.round (loopsize * (max - min) / 128. + min);
+    loopsize = Math.pow (2, loopsize);
+        
+    var bpm = engine.getValue (other?otherchctl:chctl, "bpm");
+    var rate = engine.getValue (chctl, "track_samplerate");
+    if (begin < 0)
+	begin = engine.getValue (chctl, "loop_start_position");
+    var end = begin + loopsize * 60 / bpm * rate * 2;
+    
+    engine.setValue (chctl, "loop_end_position", end);
+}
+
+MaudioXponent.syncloopParam = function(channel, control, value, status) {
+    var currentdeck = channel+1;
+    MaudioXponent.state["syncloopval"+currentdeck] = value;
+    if (MaudioXponent.state["syncloop"+currentdeck] == 1)
+	MaudioXponent.syncloopSet (channel, control, value, status, -1, 0);
+    if (MaudioXponent.state["syncloop"+currentdeck] == 2)
+	MaudioXponent.syncloopSet (channel, control, value, status, -1, 1);
+}
+
 MaudioXponent.scrmode = function(channel, control, value, status) {
     var currentdeck = channel+1;
     if (MaudioXponent.state["scrmode"+currentdeck] == 1) {

=== modified file 'mixxx/res/midi/M-Audio_Xponent.midi.xml'
--- mixxx/res/midi/M-Audio_Xponent.midi.xml	2010-12-24 16:19:41 +0000
+++ mixxx/res/midi/M-Audio_Xponent.midi.xml	2011-04-12 10:32:50 +0000
@@ -876,6 +876,60 @@
       </options>
     </control>
     <control>
+      <group>[Channel2]</group>
+      <key>MaudioXponent.syncloopOther</key>
+      <status>0x91</status>
+      <midino>14</midino>
+      <options>
+        <Script-Binding/>
+      </options>
+    </control>
+    <control>
+      <group>[Channel2]</group>
+      <key>MaudioXponent.syncloop</key>
+      <status>0x91</status>
+      <midino>15</midino>
+      <options>
+        <Script-Binding/>
+      </options>
+    </control>
+    <control>
+      <group>[Channel2]</group>
+      <key>MaudioXponent.syncloopParam</key>
+      <status>0xB1</status>
+      <midino>15</midino>
+      <options>
+        <Script-Binding/>
+      </options>
+    </control>
+    <control>
+      <group>[Channel1]</group>
+      <key>MaudioXponent.syncloopOther</key>
+      <status>0x90</status>
+      <midino>14</midino>
+      <options>
+        <Script-Binding/>
+      </options>
+    </control>
+    <control>
+      <group>[Channel1]</group>
+      <key>MaudioXponent.syncloop</key>
+      <status>0x90</status>
+      <midino>15</midino>
+      <options>
+        <Script-Binding/>
+      </options>
+    </control>
+    <control>
+      <group>[Channel1]</group>
+      <key>MaudioXponent.syncloopParam</key>
+      <status>0xB0</status>
+      <midino>15</midino>
+      <options>
+        <Script-Binding/>
+      </options>
+    </control>
+    <control>
       <group>[Flanger]</group>
       <key>lfoDepth</key>
       <status>0xB1</status>

# Begin bundle
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
ukKQXA==
------------------------------------------------------------------------------
Forrester Wave Report - Recovery time is now measured in hours and minutes
not days. Key insights are discussed in the 2010 Forrester Wave Report as
part of an in-depth evaluation of disaster recovery service providers.
Forrester found the best-in-class provider in terms of services and vision.
Read this report now!  http://p.sf.net/sfu/ibm-webcastpromo
_______________________________________________
Mixxx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mixxx-devel

Reply via email to