Skip to main content
Topic: Swing (Read 18360 times) previous topic - next topic

Swing

Inspired by Eric's Unjazzify tool and these threads -

https://forum.noteworthycomposer.com/?topic=2335.msg12893#msg12893
https://forum.noteworthycomposer.com/?topic=6032.0
https://forum.noteworthycomposer.com/?topic=5806
https://forum.noteworthycomposer.com/?topic=5590

I have written a tool to apply a variable amount of swing to tunes.

Code: [Select · Download]
<?php
/*******************************************************************************
kbsc_Swing.php Version 1.00

This script creates a tempo staff

Copyright © 2009 by K.B.S. Creer.
All Rights Reserved

HISTORY:
================================================================================
[2009-02-11] Version 1.00: Initial release
*******************************************************************************/
require_once("lib/nwc2clips.inc");

function help_msg_and_exit()
{
echo 'Called as:

php\php.exe scripts\kbsc_Swing.php "/swing=<PROMPT:Swing as percentage:=*help>"

This User Tool creates a tempo staff to apply "swing" to a melody.
The amount of time applied to two notes adding up to one crotchet (quarter note) is split between the two notes according to a parameter specifying the percentage of the time to be applied to the first note.
The result is the same regardless of whether the notes are pair of quavers (eighth notes) or a dotted crotchet + a semi-quaver.
Triplets are left unchanged.

The tool works for tunes in 2/2, 4/4, 6/8, 9/8 and 12/8. In compound time, the process is applied to the first two quavers (eighth notes) of each set of three, the third remaining unchanged.

IMPORTANT - the process overwrites the melody. Create a copy of the melody in a new staff and apply the tool to that.' ;
exit(NWC2RC_REPORT) ;
}

function error_msg_and_exit_1($a)
{
echo "Error - $a. Number in range 5 to 95 required";
exit(NWC2RC_REPORT) ;
}
function error_msg_and_exit_2($a)
{
if ($a=="") {
echo "Error - Need a Time Signature. 2/2. 4/4, 6/8, 9/8 and 12/8 only";
} else {
echo "Error - Can't process Time Signature  $a. - 2/2, 4/4, 6/8, 9/8 and 12/8 only";
}
exit(NWC2RC_REPORT) ;
}

define("QUARTER", 16);
define("EIGHTH", 8);
define("SIXTEENTH", 4);

$noteTypes = array ("Note", "Rest", "Chord", "RestChord"); //Items to process
$durArray = array ( "Whole" => 64, "Half" => 32, "4th" => 16, "8th" => 8, "16th" => 4, "32nd" => 2, "64th" => 1) ;
$durArray2 = array_flip ($durArray);
$partBarNotes = array ();
$tempo=120;
$beatLen=0;
$barDuration=0;
$beatsPerBar = 0;
$leadingNotes = TRUE;
$compound = FALSE;
$timeSig="";

function getDuration ($dur) {
//Given a Dur array, returns the duration of the note in sixteenth notes. e.g. crotchet (quarter note) = 16, dotted crotchet = 24
global $durArray;

$noteFactor=1;
$noteBase=1;
foreach ($dur as $key => $val) {
switch ($key) {
case "Triplet":
break;
case "Grace":
$noteBase=$durArray[$key];
break;
case "Dotted":
$noteFactor=3/2;
break;
case "DblDotted":
$noteFactor=7/4;
break;
case "Staccato":
break;
case "Tenuto":
break;
case "Accent":
break;
case "Slur":
break;
default:
$noteBase=$durArray[$key];
break;
}
}
$noteDuration=$noteBase*$noteFactor;

return $noteDuration;
}

function getBar (&$clipItems) {
/*******************************************************************************
Return an array of durations of notes from the current position up to the next bar line.
Quaver triplets are output as a single note with crotchet duration.
Notes longer than a crotchet are broken up as if they were built up of tied notes with maximum duration a crotchet.
Notes shorter than a quaver are combined into quavers
*******************************************************************************/
global $tempo, $beatLen, $barDuration,  $beatsPerBar, $leadingNotes, $noteTypes;

$notesInBeat = array ();
$barDuration = 0;
$tripletDur = 0;
$item=current($clipItems);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
while  ($item && $oType!="Bar") {
if ($oType=="Tempo") {
$tempo = $o->GetTaggedOpt("Tempo");
} elseif ($oType=="Ending") {
$leadingNotes = FALSE;
echo $item;
} elseif (in_array($oType, $noteTypes)) {
$opts = $o->GetOpts();
if (isset($opts["Dur"]["Triplet"])) {
$m=getDuration ($opts["Dur"]);
$tripletDur+=$m;
if ($opts["Dur"]["Triplet"]=="End") {
$tripletDur=$tripletDur * 2 / 3;
if ($tripletDur==$beatLen*2) { //crotchet triplet split into two crotchets
$notesInBeat[] = $beatLen;
$notesInBeat[] = $beatLen;
$barDuration+=$beatLen*2;
} else {
$notesInBeat[] = $tripletDur;
$barDuration+=$tripletDur;
}
$tripletDur = 0;
}
} else {
$duration=getDuration ($opts["Dur"]);
if ($duration<$beatLen/4) {
$littleNotesDur=$duration;
while ($littleNotesDur<$beatLen/2) {
$item=next($clipItems);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
if (in_array($oType, $noteTypes)) {
$opts = $o->GetOpts();
$duration=getDuration ($opts["Dur"]);
$littleNotesDur+=$duration;
}
$notesInBeat[] = $littleNotesDur;
$barDuration +=  $littleNotesDur;
}
} else {
while ($duration>$beatLen) {
$notesInBeat[] = $beatLen;
$barDuration +=  $beatLen;
$duration -= $beatLen;
}
$notesInBeat[] = $duration;
$barDuration += $duration;
}
}
}

$item=next($clipItems);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
}

return $notesInBeat;
}

function createMPC($pBN,$pc) {
global $tempo, $beatLen;
$MCPstr="|MPC|Controller:tempo|Style:Absolute|TimeRes:Sixteenth|SweepRes:1";
$offset = 0;
$Pt = 1;
$note =  array_shift ($pBN);
if ($note>=QUARTER) {
$MCPstr.="|Pt$Pt:$offset,$tempo";
} else {
if ($note>0) {
$N=round(100 * $note * $tempo / (QUARTER * $pc));
$MCPstr.="|Pt$Pt:$offset,$N";
$Pt += 1;
$offset = $note / 4;
}
$pc = 100 - $pc;

$note =  array_shift ($pBN);
if (isset($note)) {
if ($note>0) {
$N=round(100 * $note * $tempo / (QUARTER * $pc));
$MCPstr.="|Pt$Pt:$offset,$N";
$Pt += 1;
$offset = $note / 4;
}

$note =  array_shift ($pBN);
if (isset($note)) {
$MCPstr.="|Pt$Pt:$offset,$tempo";
}
}
}

$MCPstr.="|Pos:-5|Wide:N|Justify:Left|Placement:BestFit|Color:0|Visibility:Default";
return $MCPstr;
}

function createRest($dur) {
global $durArray2;
if ($dur % 7) { //not divisible by 7 therefore not DblDotted
if ($dur % 3) { //not divisible by 3 therefore not Dotted
$durCode="";
} else { // Dotted
$durCode=",Dotted";
$dur=$dur * 2 / 3;
}
} else {
$durCode=",DblDotted";
$dur=$dur * 4 / 7;
}
$durCode=$durArray2[$dur] . $durCode;


$RestStr="|Rest|Dur:" . $durCode . "|Color:0|Visibility:Default";
return $RestStr;
}

function createBeat (&$barNotes, $partLength, $percentage) {
global $barDuration;
$partBarNotes = array ();
$partDur=0;
$j=0;
while ($partDur<$partLength && $j < 10) {
$noteDur=array_shift($barNotes);
$partBarNotes[] = $noteDur;
$partDur += $noteDur;
$j+=1;
}
$barDuration -= $partDur;
echo createMPC($partBarNotes, $percentage) . "\n";
echo createRest($partDur) . "\n";
}

//Get parameters

$clip = new NWC2Clip('php://stdin');

array_shift($argv) ; // get rid of the first arg (script name)
foreach ($argv as $arg) {
if ($arg == "help") help_msg_and_exit() ;
if (preg_match('/^(\d+)$/',$arg,$m)) {
$percentage = $m[1];
if ($percentage > 95 || $percentage < 5) {
error_msg_and_exit_1($percentage);
}
} else {
error_msg_and_exit_1($arg);
}
}

//Start of main processing

echo $clip->GetClipHeader()."\n";

//Read stuff before first note (clef, key, time sig etc.) and write it back to Noteworthy
$tempo=120;
$item=current($clip->Items);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
while  ($item && !in_array($oType, $noteTypes)) {
if ($oType=="TimeSig") {
$timeSig = trim($o->GetTaggedOpt("Signature"));
if ($timeSig=="Common") {
$timeSig = "4/4";
} else {
if ($timeSig=="AllaBreve") {
$timeSig = "2/2";
}
}
if (preg_match('/^(\d+)\/(\d+)$/',$timeSig,$m)) {
$timeSigNum = $m[1];
$timeSigDen = $m[2];
$barLength=$timeSigNum * (64 / $timeSigDen);
}
}
if ($oType=="Tempo") {
$tempo = $o->GetTaggedOpt("Tempo");
}

echo $item;

$item=next($clip->Items);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
}

//Current Item is the first 'note' of the tune

if ($timeSig=="4/4" || $timeSig=="2/2") {
$beatsPerBar = 4;
$beatLen=16;
} elseif ($timeSig=="6/8") {
$beatsPerBar = 2;
$beatLen=24;
} elseif ($timeSig=="9/8") {
$beatsPerBar = 3;
$beatLen=24;
} elseif ($timeSig=="12/8") {
$beatsPerBar = 4;
$beatLen=24;
} else {
error_msg_and_exit_2($timeSig);
}

$barNotes=getBar($clip->Items);

$i=0;
while ($barDuration && $i < 100) {
if ($barDuration<$beatLen*$beatsPerBar) { //incomplete bar
if ($leadingNotes) {
if ($barDuration % $beatLen) { //the first note isn't on the beat
array_unshift($barNotes, 0);
if ($beatLen==24 && $barDuration % $beatLen==EIGHTH) {
array_unshift($barNotes, 0);
$PL=$barDuration % $beatLen;
}
createBeat ($barNotes, $barDuration % $beatLen, $percentage);
}
$k=0;
while ($barDuration && $k<10) {
createBeat ($barNotes, $beatLen, $percentage);
$k += 1;
}
$leadingNotes = FALSE;
} else {
$k=0;
while ($barDuration >= $beatLen && $k<10) {
createBeat ($barNotes, $beatLen, $percentage);
$k += 1;
}
if ($barDuration) {
createBeat ($barNotes, $barDuration, $percentage);
}
$leadingNotes = TRUE;
}
} else { //complete bar
$k=0;
while ($barDuration && $k<10) {
createBeat ($barNotes, $beatLen, $percentage);
$k += 1;
}
}
$item=current($clip->Items);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
while  ($oType=="Bar") {
echo $item;
$item=next($clip->Items);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
}
$barNotes=getBar($clip->Items);
$i+=1;
}

echo NWC2_ENDCLIP."\n";

exit(NWC2RC_SUCCESS);
?>

Let me know what you think.

Bryan Creer

Re: Swing

Reply #1
G'day Brian,
haven't had a good look at it yet, but here's useful command line for it:

tool name:
Swing It - kbsc
command:
php\php.exe Scripts\kbsc_Swing.php <PROMPT:Enter weighting as a % E.G.66=#[0,100]>

Or if you'd like to copy and past to the NWC2usertiils.ini file:
Swing It - kbsc='php\php.exe Scripts\kbsc_Swing.php <PROMPT:Enter weighting as a % E.G.66=#[0,100]>'

I plays 'Bones, crumpets, coronets, floosgals, youfonymums 'n tubies.

Re: Swing

Reply #2
Hello again Brian,

I'm not very good with PHP, but I have managed to augment your script, modifying the function getBar so that it processes note combination that were giving me trouble. One lingering problem is that a 1/6+1/8,dotted comes out the same swung rhythm as 1/8,dotted+1/16.

All readers have my permission to use this partial script at their own risk.


function getBar (&$clipItems) {
/*******************************************************************************
Return an array of durations of notes from the current position up to the next bar line.
Quaver triplets are output as a single note with crotchet duration.
Notes longer than a crotchet are broken up as if they were built up of tied notes with maximum duration a crotchet.
Notes shorter than a quaver are combined into quavers
*******************************************************************************/
global $tempo, $beatLen, $barDuration,  $beatsPerBar, $leadingNotes, $noteTypes;

$notesInBeat = array ();
$barDuration = 0;
$tripletDur = 0;
$item=current($clipItems);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
while  ($item && $oType!="Bar") {
if ($oType=="Tempo") {
$tempo = $o->GetTaggedOpt("Tempo");
} elseif ($oType=="Ending") {
$leadingNotes = FALSE;
echo $item;
} elseif (in_array($oType, $noteTypes)) {
$opts = $o->GetOpts();
if (isset($opts["Dur"]["Triplet"])) {
$m=getDuration ($opts["Dur"]);
$tripletDur+=$m;
if ($opts["Dur"]["Triplet"]=="End") {
$tripletDur=$tripletDur * 2 / 3;
if ($tripletDur==$beatLen*2) { //crotchet triplet split into two crotchets
$notesInBeat[] = $beatLen;
$notesInBeat[] = $beatLen;
$barDuration+=$beatLen*2;
} else {
$notesInBeat[] = $tripletDur;
$barDuration+=$tripletDur;
}
$tripletDur = 0;
}
} else {
$duration=getDuration ($opts["Dur"]);
$beatRemainder = $beatLen - $barDuration%$beatLen; //count from current position to end of beat
if ($duration<=$beatLen/4 and $beatRemainder = 0) { // combine short notes into a beat only at start of beat

$littleNotesDur=$duration;
while ($littleNotesDur<$beatLen/2) {
$item=next($clipItems);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
if (in_array($oType, $noteTypes)) {
$opts = $o->GetOpts();
$m=getDuration ($opts["Dur"]);
$littleNotesDur+=$m;
}
$notesInBeat[] = $littleNotesDur;
$barDuration +=  $littleNotesDur;
}
} else {
while ((($barDuration%$beatLen)+$duration)>$beatLen) { // split a longer than beat note to complete beat
$notesInBeat[] = $beatRemainder;
$barDuration +=  $beatRemainder;
$duration -= $beatRemainder;

}
$notesInBeat[] = $duration;
$barDuration += $duration;
}
}
}

$item=next($clipItems);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
}

return $notesInBeat;
}

Re: Swing

Reply #3
I have further modified the script to handle Scottish Snaps and combine notes that are less than 1/4 beat:

Best regards, Roger Nathan.

**** Modified script kbsc_rn_Swing.php (replace functions getBar and createMPC with those below) ****

Swing

(1/1)

***************************************************************************
kbsc_Swing.php Version 1.02

This script creates a tempo staff

Copyright © 2009 by K.B.S. Creer and © 2012 by Roger Nathan.
All Rights Reserved

HISTORY:
================================================================================
[2009-02-11] Version 1.00: Initial release
[2012-03-05] Version 1.01: Split notes that go past end of beat. Merge quarter beats and smaller.
[2012-04-18] Version 1.02: Detect Scottish snap and flip tempi to short then long.
*******************************************************************************/

function getBar (&$clipItems) {
/*******************************************************************************
Return an array of durations of notes from the current position up to the next bar line.
Quaver triplets are output as a single note with crotchet duration.
Notes longer than a crotchet are broken up as if they were built up of tied notes with maximum duration a crotchet.
Notes shorter than a quaver are combined into quavers
*******************************************************************************/
global $tempo, $beatLen, $barDuration,  $beatsPerBar, $leadingNotes, $noteTypes;

$notesInBeat = array ();
$barDuration = 0;
$tripletDur = 0;
$item=current($clipItems);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
while  ($item && $oType!="Bar") {
if ($oType=="Tempo") {
$tempo = $o->GetTaggedOpt("Tempo");
} elseif ($oType=="Ending") {
$leadingNotes = FALSE;
echo $item;
} elseif (in_array($oType, $noteTypes)) {
$opts = $o->GetOpts();
if (isset($opts["Dur"]["Triplet"])) {
$m=getDuration ($opts["Dur"]);
$tripletDur+=$m;
if ($opts["Dur"]["Triplet"]=="End") {
$tripletDur=$tripletDur * 2 / 3;
$notesInBeat[] = $tripletDur;
$barDuration+=$tripletDur;
$tripletDur = 0;
}
} else {
$duration=getDuration ($opts["Dur"]);
$beatRemainder = $beatLen - $barDuration%$beatLen; //count from current position to end of beat
if ($duration<=$beatLen/4 and $beatRemainder = 0) { // combine short notes into a beat only at start of beat
$littleNotesDur=$duration;
while ($littleNotesDur<$beatLen/2) {
$item=next($clipItems);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
if (in_array($oType, $noteTypes)) {
$opts = $o->GetOpts();
$m=getDuration ($opts["Dur"]);
$littleNotesDur+=$m;
}
$notesInBeat[] = $littleNotesDur;
$barDuration +=  $littleNotesDur;
}
} else {
while ((($barDuration%$beatLen)+$duration)>$beatLen) { // split a longer than beat note to complete beat
$notesInBeat[] = $beatRemainder;
$barDuration +=  $beatRemainder;
$duration -= $beatRemainder;
}
$notesInBeat[] = $duration;
$barDuration += $duration;
}
}
}

$item=next($clipItems);
$o = new NWC2ClipItem($item);
$oType = $o->GetObjType();
}

$collect = 0;
$notesInBar2 = array();
foreach ($notesInBeat as $duration) { // consolidate shorter notes into quarter beats (e.g. 1/16s)
  if ($duration+$collect <= $beatLen/4 && array_sum($notesInBar2)%($beatLen/4) == 0) {
    $collect += $duration;
  } else {
    if ($collect > 0) {
      $notesInBar2 [] = $collect;
    }
    $collect = $duration;
  }
}
if ($collect > 0) {
  $notesInBar2 [] = $collect;
}
// ***
//print_r ($notesInBar2); reset ($notesInBar2);
// ***

$collect = 0;
$notesInBar3 = array();
foreach ($notesInBar2 as $duration) { // consolidate paired quarter beats into half beats (e.g. 1/8s)
  if ($duration == $beatLen/4 && $duration+$collect <= $beatLen/2 && array_sum($notesInBar3)%($beatLen/2) == 0) {
    $collect += $duration;
  } else {
    if ($collect > 0) {
      $notesInBar3 [] = $collect;
    }
  $collect = $duration;
   }
}   
if ($collect > 0) {
  $notesInBar3 [] = $collect;
}
// ***
//print_r ($notesInBar3); reset ($notesInBar3);
// ***

return $notesInBar3;
}

function createMPC($pBN,$pc) {
global $tempo, $beatLen;
$MCPstr="|MPC|Controller:tempo|Style:Absolute|TimeRes:Sixteenth|SweepRes:1";
$offset = 0;
$Pt = 1;
$note =  array_shift ($pBN);
if ($note>=QUARTER) {
$MCPstr.="|Pt$Pt:$offset,$tempo";
} else {
if ($note>0) {
$note2 = current ($pBN);
if ($note==$beatLen/4 && $note2==$beatLen/4*3)
{
$pc = 100 - $pc;
}
$N=round(100 * $note * $tempo / (QUARTER * $pc));
$MCPstr.="|Pt$Pt:$offset,$N";
$Pt += 1;
$offset = $note / 4;
}
$pc = 100 - $pc;

$note =  array_shift ($pBN);
if (isset($note)) {
if ($note>0) {
$N=round(100 * $note * $tempo / (QUARTER * $pc));
$MCPstr.="|Pt$Pt:$offset,$N";
$Pt += 1;
$offset = $note / 4;
}

$note =  array_shift ($pBN);
if (isset($note)) {
$MCPstr.="|Pt$Pt:$offset,$tempo";
}
}
}

$MCPstr.="|Pos:-5|Wide:N|Justify:Left|Placement:BestFit|Color:0|Visibility:Default";
return $MCPstr;
}



Re: Swing

Reply #4
Roger Nathan sent me the following :

I've made some updates to handle long notes that have a swing component,
short notes that need to be treated as a quarter beat and Scottish Snaps.

As I had difficulty updating my NWC Forum membership, I have only been
able to post code fragments, but I would like to post the entire revised
script.

I have attached the script and a test file, hoping that you could make
it available to anyone who needs it.


So here they are .

Rich.

Re: Swing

Reply #5
Hi I'd like to run these for the Scottish Snaps which are a big part of our Celtic Fiddling. However I am not sure how to install it and how to set it up to run. Can any one give ne some assistance.  I hae a Tune that is written as a reel and there is another version that is a Strathspey.  I'd like to use the Reel and see how this might convert to closer to the Strathspey.

Any Suggestions on how to run ?

John