Note that this Wiki is a work in progress, items may not be updated or may disappear entirely as the pages are updated.

Custom Song Balance Script

From SpacialAudio

Jump to: navigation, search

{
CUSTOM SONG SCORING SCRIPT 
BY TOBY SHEETS (tobasco@riograndemud.com)
FOR SAM USERS

Edited 2/28/03

--------------------------------------------------------------------------------
Overview:

This script overrides SAM's 'balance' field values to provide a more
robust and accurate song scoring system. It has so far only been tested
using the category-based rotation logic. Here's how it works:

Every time a song plays, this script runs. The very first thing it does
is set all song balances to zero. Then it applies a series of rules to each
song based on various parameters. Every time a rule is broken, the balance
value of the song is incremented by a set value. Once the script has completed
its check of rules, the songs all have a final balance. The lower the balance
of a song, the better choice that song is for next play because it has 
broken the fewest rules to the least degree.
 
When using category based rotation to control your station you typically use
a line like:

 Cat.QueueBottom('My_Category',smWeighted, prNoRules);

The parameter 'smWeighted' means SAM will choose the song with the lowest 
balance from the category you specify. Because WE are now controlling the
balances of songs we are forcing SAM to make better decisions when selecting
songs. You should see much better song selection using this PAL script. 

The beauty of this script is that it can be added on to quite easily. I will
be creating more rules and adding them to this script as time goes by with
suggestions from you all.

Happy broadcasting!


 

 PAL.Loop := True;


 // Set some global variables to be used throughout the script

   var
    D, D2, D3 : TDataSet;

   var
    scale, points, increments,
    highest_point, songID, count : Integer;
   var
    lowest_point, point_increment, balance, penalty : Float;
    
   var
    date : DateTime;


   // SET ALL SONG balanceS TO ZERO

   ExecSQL('UPDATE songlist SET balance = 0',[]);


 {*******************************************************************************
               START OF RULE 1
 *******************************************************************************}

   var current_time :  DateTime = now;
   var three_hours_ago :  DateTime = T['-03:00:00'];

 {******************************************************************************
 RULE 1: NO SONG REPEATS WITHIN 3 HOURS
 This rule applies severe penalties to songs played in the last three hours.
 The most recently played tune receives the heaviest penalty. A song played
 2 1/2 hours ago receives a lighter penalty. For every second closer to NOW that
 a song played, its penalty increases by about 9.25 points.
 ******************************************************************************}
   Pal.LockExecution;

 // SET PENALTY VARIABLES

 scale := 100;
 points := 100;
 increments := 10800;      // the number of sec's in 3 hours
 lowest_point := points - ((points / 100) * scale);
 highest_point := points;
 point_increment := (highest_point - lowest_point) / increments;


 D:=Query('SELECT ID, date_played FROM songlist WHERE date_played > :now', [three_hours_ago], True);
 D.First;

 While Not D.EOF Do
 Begin
  // SET NEW balanceS FOR ALL SONGS PLAYED WITHIN LAST 3 HOURS
  date := D['date_played'];
  penalty := ((((date * 86400) - (three_hours_ago * 86400)) * point_increment) + lowest_point);
  ExecSQL('UPDATE songlist SET balance = balance + :penalty WHERE ID = :ID', [penalty, D['ID']]);
  D.Next;
 End;

 D.Free;
 Pal.UnlockExecution;
 WriteLn('Rule 1 complete.');

 {*******************************************************************************
               END OF RULE 1
 *******************************************************************************}

 

 

 {*******************************************************************************
               START OF RULE 2
 *******************************************************************************}

   var lastgenre : String;


 {*******************************************************************************
 RULE NUMBER 2: DIFFERENT GENRE FOR EACH PLAY
 Checks genre of the currently playing song and penalizes all songs that are
 from the same genre. This just adds a little variety, especially if you are
 running a Top 40 station. For instance it will make sure you don't have two
 rap songs back to back, or two pop songs. For this to really work well you 
 should take the time to go through all of your songs and fine tune the genre 
 tags. Instead of using the "Rock" genre for all of your fast guitar songs, 
 sort them into "Hard Rock", "Metal", "Death Metal" etc. Sort your "Rap" genres
 into "Hip Hop", "Freestyle", "Gangsta", etc.

 Also, this rule only works if you leave SAM on "Ghost Queue" mode, instead
 of keeping more than one track in the queue. This rule only applies to the 
 currently playing song therefore, if you're adding the 5th song to the queue, 
 only the 5th song will be of a different genre from the one currently playing. 
 *******************************************************************************}
   Pal.LockExecution;

   penalty := 20;

   // GET GENRE OF LAST SONG PLAYED

   D := Query('SELECT genre FROM songlist ORDER BY date_played DESC LIMIT 1', [], True);
     lastgenre := (D['genre']);
   D.Free;

   // PENALIZE ALL SONGS WITH MATCHING GENRE
   ExecSQL('UPDATE songlist SET balance = balance + :penalty WHERE genre = :lastgenre',[penalty, lastgenre]);

   PAL.UnlockExecution;
   WriteLn('Rule 2 complete.');
 {*******************************************************************************
               END OF RULE 2
 *******************************************************************************}

 

 

 {*******************************************************************************
               START OF RULE 3
 ********************************************************************************
 ********************************************************************************
 RULE 3: NO ARTIST REPEATS WITHIN AN HOUR
 All artists that have played in the last 60 minutes will be penalized. Currently
 a blanket value of 100 points is assigned to all artists played in the last hour.

 (This rule could be made better by checking exactly how long ago that artist was
 played and penalizing it differently for each second closer to NOW that the 
 artist was played - much like the way RULE 1 penalizes songs.I'll probably play
 with this a bit after some coffee.)
 *******************************************************************************}

 Pal.LockExecution;

 penalty := 100;

 //GRAB ALL ARTISTS FROM LAST HOUR

 D := Query('SELECT DISTINCT artist FROM historylist WHERE date_played > :now ORDER BY date_played DESC',[T['-01:00:00']],True);

 D.First;
 while not D.EOF do
 begin
   ExecSQL('UPDATE songlist SET balance = balance + :penalty WHERE artist = :artist',[penalty, D['artist']]);
   D.Next;
 end;

 D.Free;

 PAL.UnlockExecution;
 WriteLn('Rule 3 complete.');

 {*******************************************************************************
                  END OF RULE 3
 ********************************************************************************

 

 {*******************************************************************************
               START OF RULE 4
 ********************************************************************************
 ********************************************************************************
 RULE 4: CHECK QUEUE LIST
 This rule double checks the queue list. Any songs that are already queued will 
 be assigned some penalty points (currently set at 100). 
 *******************************************************************************}
   Pal.LockExecution;
   penalty := 100;

   //GRAB ALL SONGS FROM QUEUE LIST
   D := Query('SELECT songID FROM queuelist',[],True);
   D.First;
    while not D.EOF do
    begin
      D3 := Query('SELECT artist from songlist WHERE ID = :songID', [D['songID']], True);
      ExecSQL('UPDATE songlist SET balance = balance + :penalty WHERE artist = :artist',[penalty, D3['artist']]);
      D3.Free;
      D.Next;
    end;
   D.Free;


   PAL.UnlockExecution;
   WriteLn('Rule 4 complete.');
 {*******************************************************************************
               END OF RULE 4
  ********************************************************************************}

 

 


 {*******************************************************************************
               START OF RULE 5
 ********************************************************************************}

 var x : Integer;

 {*******************************************************************************
 RULE 5: Check number of plays
 This rule checks how many times each song has played. The more a song has been
 played the higher the penalty will be. If a song has never played it will end up
 with a lower balance than the one that has played the most.
 ********************************************************************************}


 PAL.LockExecution;

 scale := 100;
 points := 100;


 // Get highest number of song plays
 D := Query('SELECT DISTINCT count_played from songlist ORDER BY count_played DESC LIMIT 1', [], True);

 increments := D['count_played'];
 D.Free;
 lowest_point := points - ((points / 100) * scale);
 highest_point := points;
 point_increment := (highest_point - lowest_point) / increments;

 ExecSQL('UPDATE songlist SET balance = balance + (count_played * :point_increment) ', [point_increment]);

 PAL.UnlockExecution;
 WriteLn('Rule 5 complete.');

 {*******************************************************************************
               END OF RULE 5
 ********************************************************************************}

 

 

 

 {*******************************************************************************
               START CHOOSE BEST SONG
 ********************************************************************************
 ********************************************************************************
 This section grabs the song with the lowest balance. If more than one song 
 shares the lowest balance, a random one will be chosen with that score.

 This section is currently disabled. If you do not have a complex script set up
 for category based rotation, you can enable this section to do the song
 selection for you. You will be amazed at the consistent results! To enable this
 section, just put a closed bracket at the end of the line below this text, just
 after the long series of asterisks.
 *******************************************************************************}

   WriteLn('Choosing best song...');
   Pal.LockExecution;

  // GRAB LOWEST FINAL balance

   D := Query('SELECT balance FROM songlist ORDER BY balance ASC', [], True);
     balance := D['balance'];
   D.Free;

   // Count number of songs with this balance
   D:= Query('SELECT filename, balance FROM songlist WHERE balance = :balance ORDER BY RAND() LIMIT 1', [balance], True);

   // Que up selected song 

   Queue.AddFile(D['filename'],ipBottom);
   WriteLn('Adding: ' + D['filename'] + ' to queue.');


   D.Free;
   PAL.UnlockExecution;

 {*******************************************************************************
               END CHOOSE BEST SONG
 *******************************************************************************}

   // Wait for song to play before running again from the top
   PAL.WaitForPlayCount(1);

Personal tools