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
{
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);
