Facendo doppio click sulla riga, si aprirà il PAL Scripting IDE, ovvero l’area di
programmazione interna alla regia.
Vediamo un esempio:
var T : Integer;
for T := 1 to 3 do
WriteLn(‘WriteLn ‘+IntToStr(T));
FOR T := 1 to 3 do
Begin
WriteStr(‘WriteStr ‘+IntToStr(T));
end;
var
a,
b,
c:Integer;
a:=1;b:=2;c:=3;
writeln(a);
writeln(b);
writeln(c);
Per ora non concentriamoci su cosa faccia il programma, ma sulla struttura dei blocchi di
codice:
♦ le istruzioni semplici, quelle che FANNO qualcosa terminano sempre col punto e
virgola
♦ la struttura begin/end racchiude e definisce i blocchi di istruzioni
♦ le istruzioni che aprono un blocco o che ricevono un blocco non terminano mai col
punto e virgola
♦ le istruzioni che chiudono un blocco terminano sempre col punto e virgola.
Un blocco si comporta come fosse un’istruzione, un blocco costituito da una sola
istruzione può non essere racchiuso (ovviamente).
Le istruzioni sono delimitate dal punto e virgola, e solo da esso. Questo implica che:
♦ un’istruzione può estendersi su più linee
♦ più istruzioni possono stare sulla stessa linea.
Notiamo inoltre che:
PAL NON È CASE SENSITIVE
Non differenzia maiuscole e minuscole. Né nei nomi delle variabili, né nelle keyword.
In PAL i nomi degli identificatori sono composti da una lettera alfabetica seguita da una
combinazione libera di altre lettere e cifre. L’unico simbolo non alfabetico ammesso è
l’underscore (“_”). Inoltre:
NON È POSSIBILE UTILIZZARE CARATTERI ACCENTATI O ALTRI CARATTERI
NON STANDARD PER DEFINIRE DEI “SIMBOLI” UN IDENTIFICATORE NON
PUÒ COMINCIARE CON UN NUMERO
1.2.2. I commenti
I commenti sono delle stringhe che si inseriscono dentro il programma ma non ne alterano
il funzionamento, l’unico loro scopo è appunto “commentare” il codice per renderlo più
leggibile in seguito. È sempre una buona cosa commentare il codice, torneremo su questo
argomento parlando di coding guidelines.
Vediamo adesso come PAL definisce i commenti. Ne esistono di due tipi: a linea singola e
multi linea:
// Questo è un commento a linea singola
{
Questo è un commento
multilinea
}
Si possono dichiarare più variabili dello stesso tipo nella stessa istruzione:
var a,b,c:Integer;
Invece, per cambiare il valore di una variabile a runtime si usa l’operatore “:=”:
d:=42;
Il tipo di una variabile definisce l’insieme dei valori che la variabile può assumere. PAL
definisce un insieme di tipi primitivi (i tipi definiti nel linguaggio) che sono:
TIPI SEMPLICI
TIPI COMPOSTI
A questi PAL aggiunge molti altri tipi e oggetti che permettono di manipolare strutture
complesse e interagire con la regia.
Un tipo particolare di cui è meglio parlare subito è il non-tipo Variant.
1.2.3.1. Il tipo variant
Il tipo Variant è un workaround alla tipizzazione forte del Pascal/Delphi e avrebbe
inorridito il povero Niklaus Wirth.
Permette infatti di definire una variabile senza tipo e che può addirittura “cambiare tipo”
durante l’esecuzione del programma.
Questa “magia” viene resa possibile dall’interprete che deve capire in tempo reale il tipo
della variabile in base al valore che viene impostato. Questo ha delle ricadute sia sui tempi
di esecuzione sia sull’occupazione di memoria.
01 { Variabili }
02 var V1, V2, V3, V4, V5: Variant;
03 var I: Integer;
04 var D: Real;
05 var S: string;
06 { Istruzioni }
07 V1 := 1; { intero }
08 V2 := 1234.5678; { float }
09 V3 := ‘Hello world!’; { stringa }
10 V4 := ‘1936’; { stringa }
11 V5 := V1 + V2 + V4; { float 2235.5678}
12 I := V1; { I = 1 (intero) }
13 D := V2; { D = 1234.5678 (float) }
14 S := V3; { S = ‘Hello world!’ (stringa) }
15 I := V4; { I = 1936 (intero) }
16 S := V5; { S = ‘2235.5678’ (stringa) }
Come vediamo in questo esempio, le variabili V1, V2, V3, V4, V5 sono definite senza
tipo e il loro tipo varia durante l’esecuzione del programma.
Nel caso di assegnazione di un Variant a una variabile a tipo fisso l’interprete cerca di
convertire il contenuto del Variant nella variabile destinazione (riga 15).
Se una di queste conversioni è impossibile si verifica un errore di runtime. Per questo
bisogna usare le Variant con cautela.
1.2.4. I letterali
I letterali sono il modo in cui è possibile inizializzare una variabile dal codice. Tutti i tipi
primitivi hanno un modo per essere inizializzati.
Abbiamo già visto i numeri interi e i numeri in virgola mobile, che hanno una sintassi
ovvia.
Vediamo adesso gli altri tipi.
STRINGHE
Le stringhe vengono racchiuse fra apici singoli, nel caso si voglia usare un apice singolo
all’interno di una stringa lo si deve “duplicare”.
È possibile includere dei caratteri specificando il codice del carattere dentro la stringa
attraverso il carattere # seguito dal codice in decimale o esadecimale (preceduto da $):
var s1, s2, s3 : String;
s1 := ‘Questa è una stringa’;
s2 := ‘Nell’’anfratto della grotta trentatré gretti gatti si grattano’;
s3 :=‘Hello’#13#$0D’World’;
I booleani possono assumere solo due valori, i simboli di questi due valori sono true e
false.
Quando si converte un Boolean in un intero, true = 1 e false = 0.
Quando si converte un intero a stringa 0 -> false e ∀x>0 -> true.
Riassumiamo con del codice. Un codice vale più di mille parole.
var s1, s2 : String;
var r1,r2 : Float;
var i1,i2 : Integer;
var b1,b2 : Boolean;
s1 := ‘Questa è una stringa’;
s2 := ‘Nell’’anfratto della grotta trentatré gretti gatti si grattano’;
r1 := 0.07;
r2 := 7e-2; //Notazione esponenziale 7*10^-2= 0.07
b1 := TRUE;
b2 := FALSE;
1.2.5. Le costanti
Le costanti permettono di associare un nome a un valore. A differenza delle variabili il
valore non può cambiare e viene definito in fase di dichiarazione. La dichiarazione delle
costanti segue questo schema:
const Nome = Valore;
1.2.6. Operatori
Gli operatori sono “qualcosa” che esegue un’operazione su uno o due operandi restituendo
un valore. Il tipo di valore restituito varia a seconda dell’operatore e degli operandi
utilizzati. La differenza fra un operatore e una funzione è che l’operatore è definito nel
linguaggio mentre una funzione è definita nel codice.
Il tipo restituito da un operatore dipende sia dagli operandi che dall’operatore stesso.
Bisogna fare molta attenzione, PAL è un linguaggio a tipizzazione forte.
var r1,r2,r3: Float;
var i1,i2,i3: Integer;
r1:=0.1;
r2:=0.2;
r3:=r1*r2; // 0.02, assegno ad un altro Float, si può fare.
i1:=r2/r1; // 2, ma assegno un float ad un intero, non si può fare, MAI.
i2:=Round(r2/r1); // 2, convertito a intero. Si può fare.
A + B Somma
A - B Sottrazione
A * B Prodotto
A := B Assegnazione
L’ultima istruzione che compone un blocco può non terminare col punto e virgola.
Ignoreremo quasi sempre questa possibilità.
Il punto e virgola termina tutte le strutture. Nel caso una struttura venga passata a un’altra,
la struttura passata non va terminata col punto e virgola.
Vedremo questa regola applicata in tutti i blocchi seguenti.
1.2.7.2. Strutture condizionali
Le strutture condizionali sono le seguenti:
IF-THEN-ELSE
Notate che l’end prima di un else non ha il punto e virgola. Il punto e virgola termina
l’intera struttura. Nel caso manchi l’else la struttura assume questa forma:
CASE-OF
In questo caso, la variabile “mese” viene confrontata con i numeri da 1 a 12. Ovviamente
ogni condizione può ricevere un blocco.
L’else finale permette di definire una condizione che si verifica quando nessuna delle
precedenti si è verificata. Case permette inoltre di accorpare diverse condizioni:
…
var mese : Integer;
…
case mese of
1,2 : Writeln( ‘Fa’ molto freddo!’ );
3,4 : Writeln( ‘Piove!’ );
5 : Writeln( ‘Boh, è Maggio.’ );
6..9 : Writeln( ‘Fa’ caldo!’ );
10..12: Writeln( ‘Fa’ freddo!’ );
end;
Il blocco di istruzioni compreso fra repeat e until viene ripetuto fino a quando la
condizione dopo until non diventa vera. Il primo controllo avviene dopo un’esecuzione.
var a:integer=0;
repeat
OkCancelDlg(‘Continuerò a scocciarti finché non immetterai un numero pari’);
a:=StrToInt(Inputbox(‘Immetti un numero’,’’,’’));
until a mod 2 = 0
WHILE-DO
Il blocco di istruzioni passato a do viene ripetuto finché la condizione del while è vera. Il
primo controllo avviene prima della prima esecuzione.
var a:integer=0;
a:=StrToInt(Inputbox(‘Immetti un numero’,’’,’’));
while a mod 2 > 0 do
begin
OkCancelDlg(‘Continuerò a scocciarti finché non immetterai un numero pari’);
a:=StrToInt(Inputbox(‘Immetti un numero’,’’,’’));
end;
FOR-TO-DO
In questo caso è la funzione StrToInt che genera un’eccezione quando non sa come
convertire la stringa passatagli in un intero.
Il blocco except ci permette di ottenere informazioni sull’eccezione generata attraverso
una variabile di tipo exception (o un tipo derivato).
Vediamo come funziona in questo esempio, dove volutamente dividiamo per zero:
var n1, n0 : Integer;
try
n0 := 0;
n1 := 1;
n1 := n1 div n0;
except on E : Exception do
begin
WriteLn(‘Eccezione di tipo ‘+E.ClassName);
end;
end;
Le variabili utilizzate dalle procedure o funzioni vengono dichiarate nel solito modo e
sono separate da punto e virgola.
Il modificatore const permette di passare una costante (il cui valore non può essere
modificato all’interno della funzione).
//Potenza di un numero reale
function Power(X: Float; const Y: Integer): Float;
var
I: Integer;
begin
Result := 1.0; I := Y;
while I > 0 do
begin
if Odd(I) then Result := Result * X;
I := I div 2;
X := Sqr(X);
end;
end;
Il tipo più comune di array multidimensionale è quello a due dimensioni, noto anche come
matrice. Una matrice è costituita da una “tabella” di dati dello stesso tipo, ogni elemento è
identificato da una riga e da una colonna.
Questo concetto è espandibile su più dimensioni, quindi generalizzando possiamo dire
che:
UN ARRAY MULTIDIMENSIONALE È UN INSIEME DI DATI DELLO STESSO
TIPO A CUI SI PUÒ ACCEDERE ATTRAVERSO DEGLI INDICI.
La dichiarazione di un array multidimensionale è una diretta estensione di quella vista per
gli array monodimensionali:
var MatriceDaDieci : Array [0..10,0..10] of Float;
Gli array dinamici sono una feature importantissima di PAL. Sono concettualmente molto
simili agli array classici ma permettono di essere definiti senza la dimensione e di essere
dimensionati dinamicamente. In questo testo useremo quasi sempre array dinamici:
♦ gli array dinamici vengono dichiarati omettendone la dimensione
♦ gli array dinamici multidimensionali sono realizzati come strutture annidate di array
monodimensionali (array di array)
♦ gli array dinamici partono sempre dalla posizione 0.
var ArrayDinamico: Array of Float;
var ArrayDinamicoBidimensionale: Array of Array of Float;
Tipicamente nei linguaggi derivati da prodotti Borland i tipi si dichiarano con un nome
che inizia per “T” (ma è solo una convenzione).
UN ESEMPIO PIÙ COMPLESSO
Completiamo la trattazione della sintassi di PAL con un esempio che utilizza molte delle
funzioni descritte: l’implementazione dell’algoritmo QuickSort di Hoare.
const size = 100;
type TData = array [0 ..size - 1] of integer;
function Partition (var d: TData; i, j: integer) : integer;
var
m, tmp: integer;
begin
m := d[(i + j) div 2];
while i < j do
begin
while d[i] < m do i := i + 1;
while d[j] > m do j := j - 1;
if d[i] > d[j] then
begin
tmp := d[j];
d[j] := d[i];
d[i] := tmp;
end
else
i := j;
end;
Result := i;
end;
procedure QuickSort(var d: TData; i, j : Integer);
begin
var m : Integer;
if i < j then
begin
m := Partition (d, i, j);
QuickSort (d, i, m);
QuickSort (d, m + 1, j);
end;
end;
var data : TData;
QuickSort(data, 0, size - 1);
1.3. Le funzioni principali della libreria
1.3.1. Funzioni matematiche
Oltre agli operatori aritmetici, PAL ci mette a disposizione una serie di funzioni
matematiche.
In questo paragrafo elencheremo le principali.
function Inc(var a : Integer; b : Integer):Float; Somma due interi, converte il risultato in float
function Frac(v : Float):Float; Parte frazionaria di un float (il numero meno la sua parte intera)
function IntToHex(v : Integer; Digits : Da intero a esadecimale col numero di cifre specificate (ritorna
Integer):String; una stringa)
function Pos(SubStr : String; Str : string):Integer; Posizione di una sottostringa dentro la stringa
function TrimLeft(Str : String):String; Elimina gli spazi alla fine della stringa
function Trim(Str : String):String; Elimina gli spazi all’inizio o alla fine della stringa
function CompareText(Str1 : String; Str2 :
String):Integer; Confronta due stringhe
PAL fornisce anche un utile strumento per la gestione del tempo. La funzione si chiama T,
ed è una timemask che può esistere in diversi formati.
TEMPO STANDARD: HH:MM:SS
La maschera deve essere rappresentata in formato 24 ore e contenere tutti i campi, ore,
minuti e secondi.
Ad esempio T[‘14:00:00’] creerà un valore datetime pari a oggi alle 14.
CALCOLO DEL TEMPO: +HH:MM:SS -HH:MM:SS
XX svolge il ruolo di valore jolly. La maschera cercherà l’orario più vicino che rispetti
l’indicazione.
Ad esempio, supponendo che siano le 14:05 e che la maschera sia XX:00:00. Il tempo
risultante sarà 15:00 perché è l’orario più vicino che rispetta l’indicazione.
PROSSIMO ORARIO (FISSO)
Alcuni esempi:
WriteLn(‘—Tempo standard);
WriteLn(T[‘14:00:00’]); {## stampa le 14 di oggi}
WriteLn(T[‘04:30:00’]); {## stampa le 4:30 di oggi}
WriteLn(DateTime(T[‘09:00:00’]-1)); {## stampa le 9 di ieri}
WriteLn(DateTime(T[‘09:00:00’]+1)); {## stampa le 9 di domani}
WriteLn(‘—Calcolo’);
WriteLn(T[‘+01:00:00’]); {## stampa un’ora da adesso}
WriteLn(T[‘-00:00:30’]); {## stampa trenta secondi fa}
WriteLn(T[‘NEXT60’]); {## stampa la prossima ora intera}
WriteLn(T[‘NEXT30’]); {## stampa la prossima mezz’ora}
WriteLn(T[‘NEXT15’]); {## stampa il prossimo quarto d’ora}
WriteLn(‘—WildCards’);
WriteLn(T[‘XX:00:00’]); {## come nexthour}
WriteLn(T[‘XX:59:30’]); {## trenta secondi prima della prossima ora}
WriteLn(T[‘XX:XX:30’]); {## prossimi trenta secondi}
Alcuni esempi:
D := T[‘13:00:00’]; {Le 13:00 di oggi}
D := T[‘+00:00:10’]; {10 secondi da adesso}
D := 1 + T[‘+00:00:10’]; {Domani, dieci secondi da adesso}
ESTRARRE DA UNA DATA LE SINGOLE PARTI
var y,m,d : Integer;
var hh,mm,ss,ms : Integer;
var D : DateTime;
D := Now;
DecodeDate(D,y,m,d);
DecodeTime(D,hh,mm,ss,ms);
FUNZIONE DAYOFWEEK
function StrToDate(Str : String): DateTime; Parsa un oggetto DateTime a partire da una stringa (solo la data)
function TimeToStr(Time : DateTime):String; Converte un oggetto DateTime in una stringa (solo il tempo)
function FormatDateTime(Format : String; dt : Genera una stringa a partire da un DateTime e da una stringa di
DateTime):String; formato
procedure DecodeDate(dt : DateTime; var Scompone un oggetto DateTime nelle sue componenti come
yy,mm,dd : Integer); numeri interi (solo la data)
function EncodeDate(yy, mm, dd : Costruisce un oggetto DateTime a partire dalle sue componenti
Integer):DateTime; (solo la data)
procedure DecodeTime(dt : DateTime; var hh, Scompone un oggetto DateTime nelle sue componenti come
mm, ss, ms : Integer); numeri interi
Questi comandi causeranno uno stop dello script fin quando l’evento specificato non si
compie.
PAL.WAITFORPLAYCOUNT();
Questo comando aspetterà che vengano suonate due canzoni prima che lo script venga
ripreso. Questo viene fatto settando un counter internamente al momento in cui
WaitforPlayCount viene eseguito. Da quel momento in poi ogni brano caricato su un
deck e suonato diminuirà il contatore. Nel momento in cui il contatore diventa zero lo
script ricomincia.
PAL.WAITFORQUEUE();
Questo comando aspetterà che la coda contenga esattamente due brani. In questo modo se
la coda è composta da 4 elementi e la coda comincia a svuotarsi, lo script ricomincerà a
funzionare solo quando la coda avrà 2 elementi. È ovvio che questo comando va usato con
cautela, si immagini che questo script lavori contemporaneamente a uno script che deve
mantenere la coda con 4 elementi, ciò farebbe in modo che lo script contenente il
comando WaitForQueue resti in waiting per sempre.
PAL.WAITFORTIME(TIME);
Bisogna tenere presente che se si invia a WaitForTime una data che è passata, non ci sarà
attesa.
Questo può essere fonte di bug subdoli.
Un esempio tipico è presente in questo codice:
PAL.Loop := True;
PAL.WaitForTime(‘10:00:00’);
WriteLn(‘Sono le 10 del mattino’);
PAL.WaitForTime(‘20:00:00’);
WriteLn(‘Sono le 8 di sera’);
PAL.WaitForTime(‘23:59:59’);
Se questo script viene eseguito alle 7 del mattino, farà egregiamente il suo lavoro. SAM
aspetterà sino alle 10, quindi scriverà il messaggio, poi aspetterà sino alle 20 e ne scriverà
un altro, poi attenderà fino a un minuto prima di mezzanotte per ricominciare da capo.
Se invece lo script viene eseguito alle 17, SAM controllerà il primo WaitForTime e si
accorgerà che le 10 sono passate e immediatamente stamperà il messaggio “Sono le 10 del
mattino” senza aspettare e ovviamente all’orario sbagliato.
Un modo per risolvere questo problema può essere dato da questo codice:
var OldTime : DateTime;
OldTime := Now; PAL.Loop := True;
PAL.WaitForTime(‘10:00:00’);
if (OldTime<=T[‘10:00:00’]) then WriteLn(‘Sono le 10 del mattinò);
PAL.WaitForTime(‘20:00:00’);
if (OldTime<=T[‘20:00:00’]) then WriteLn(‘Sono le 8 di serà);
PAL.WaitForTime(‘23:59:59’);
end;
Il codice, per come è strutturato, non funzionerebbe. Il Wait non sarebbe eseguito e il
codice continuerebbe.
Occorre sostituire il codice con una soluzione differente:
// Questo è un workaround, non si può aspettare dentro un if
var temp : Boolean = True;
WHILE (A>B) AND (temp) DO
begin
PAL.WaitForPlayCount(1);
temp := False;
end;
Questo codice potrebbe sembrare folle a chi non conosce questa lacuna, quindi è bene
commentare quello che si sta facendo.
1.6. Velocizzare l’esecuzione degli script
Come accennato precedentemente, gli script PAL vengono eseguiti all’interno del thread
principale della regia. Vengono quindi eseguiti molto lentamente per evitare rallentamenti.
La velocità di esecuzione è di circa una riga al secondo. Quest’impostazione si rivela
accettabile nella maggior parte dei casi, ma ci sono situazioni in cui un secondo per
istruzione è veramente troppo. C’è un modo per velocizzare l’esecuzione.
Utilizzando l’oggetto PAL è possibile velocizzare momentaneamente l’esecuzione degli
script:
PAL.LockExecution;
{Fai qualcosa}
PAL.UnlockExecution;
È un workaround, e come tale va usato con parsimonia. Ogni volta che viene aperta la
LockExecution, SAM Broadcaster ne risente moltissimo a livello audio e di encoding.
1.7. Gli oggetti
Nonostante in questo testo ci concentriamo su un uso procedurale, PAL è un linguaggio
orientato agli oggetti. L’interazione degli script con la regia avviene attraverso degli
oggetti che rappresentano le sue componenti.
Possiamo pensare a un oggetto come a un’entità che include dei dati e le procedure per
interagire con i dati stessi.
Vediamo per esempio l’oggetto ActivePlayer che rappresenta il player attivo nella regia:
ActivePlayer.FadeToNext; //FadeToNext è un metodo
ActivePlayer.Volume := 255; //Proprietà in lettura/scrittura
WriteLn(ActivePlayer.Duration); //Proprietà in sola lettura
Il secondo parametro, ipBottom, indica che il brano deve essere inserito alla fine della
categoria; questo parametro è fondamentale nella gestione delle code più che nella
gestione delle categorie, ciononostante è settabile anche in questo caso e può essere sia in
alto (ipTop) che in basso (ipBottom).
Gli stessi comandi possono essere utilizzati per aggiungere file alla coda:
Queue.AddDir(‘c:\music\’, ipBottom);
Queue.AddFile(‘c:\music\test.mp3’, ipBottom);
Queue.AddList(‘c:\music\playlist.m3u’, ipBottom);
Queue.AddURL(‘http://localhost:8000/test.mp3’, ipBottom);
NUMERO
NOME DEL PARAMETRO OPERAZIONE EFFETTUATA
EQUIVALENTE
PAL.WaitForQueue(0);
Il primo script dovrebbe essere molto chiaro. Si utilizza alla fine il comando
WaitForQueue(0) per aspettare che la coda sia vuota.
//Script PAL numero 2
PAL.Loop := True;
PAL.WaitForPlayCount(5);
Cat[‘MyJingles’].QueueTop(smLRP, NoRules);
PAL.WaitForPlayCount(3);
Cat[‘MyPromos’].QueueTop(smLRP, NoRules);
Cat[‘MyAdz’].QueueTop(smLRP, NoRules);
Il secondo script aspetta che vengano suonati cinque brani (con il comando
WaitForPlayCount(5)), quindi inserisce all’inizio della coda un jingle, aspetta che
vengano suonati altri tre brani, quindi inserisce in cima alla coda una pubblicità e un
promo, che ovviamente, visto che sono inseriti in testa, devo essere elencati al contrario.
1.7.1.2. Regole di Playlist
I brani memorizzati all’interno delle categorie possono essere selezionati in diversi modi,
come già visto. Un brano può essere selezionato soltanto se rispetta le regole dettate dalla
logica di playlist. Queste regole mirano a limitare le ripetizioni degli stessi brani in un
periodo di tempo, sia per questioni estetiche che per motivi legali.
Le regole non dovrebbero però essere applicate a jingle, pubblicità e promo. I motivi di
tali esclusioni dovrebbero essere scontati: si tratta di file che devono essere ripetuti proprio
perché richiesto dalla radio o dai clienti, cercando comunque di mantenere una variabilità.
La variabilità dei jingle si potrebbe risolvere utilizzando la regola “smLRP” (brano meno
recentemente suonato).
1.7.1.3. Motore audio
Il motore audio è formato da diversi canali audio rappresentati dai player (deck). Il
linguaggio PAL permette di controllare varie componenti del motore audio, il che risulta
utile quando occorra dire a queste parti cosa debbano fare.
Il Deck A, il Deck B, l’Aux 1, l’Aux 2, l’Aux 3 e il modulo SoundFX sono tutti player, e
fanno parte della classe TPlayer. Voice FX non rientra tra i player perché attraverso
questo modulo dovrebbe soltanto passare voce.
1.7.1.3.1. Visualizzare informazioni su un brano in esecuzione nel Deck A
Lo script seguente visualizza le informazioni del Deck A:
var Song : TSongInfo;
Song := DeckA.GetSongInfo;
if Song = nil then
WriteLn(‘Non ci sono brani caricati sul deck A’)
else
begin
WriteLn(‘Artista: ‘+Song[‘artist’]);
WriteLn(‘Titlolo ‘+Song[‘title’]);
WriteLn(‘Album: ‘+Song[‘album’]);
WriteLn(‘Durata: ‘+Song[‘duration’]);
end;
Song.Free;
1.7.1.4.2. Encoders
Volendo semplificare estremamente, un encoder è un qualsiasi programma che converta
dei dati in ingresso aventi particolari caratteristiche in dei dati d’uscita, aventi altre
caratteristiche peculiari differenti dalle precedenti.
Quando viene convertito un file WAVE In un file MP3, il programma di conversione si
chiama proprio encoder MP3. Trasforma il file WAVE non compresso in un file
compresso che risponde alle caratteristiche di compressione dell’algoritmo MP3.
In SAM Broadcaster un encoder è quindi un componente che trasforma l’audio della regia,
sia esso musica, voce o effetti sonori, in un flusso unico, eterogeneo, con un algoritmo di
compressione definito e utilizzabile come sorgente per un server di streaming.
Viene considerata codifica una qualsiasi trasformazione di:
♦ algoritmo di compressione (ad esempio da WAV a MP3)
♦ variazione di bitrate (ad esempio da MP3 128 kbps a MP3 32 kbps)
♦ variazione numero canali (da MP3 stereo a mono).
Più raramente si parla di variazione di risoluzione e frequenza di campionamento.
Il seguente script permette di avviare tutti gli encoder alle 7 del mattino e fermarli alle 20
di sera:
PAL.Loop := True;
PAL.WaitForTime(‘07:00:00’);
Encoders.StartAll;
PAL.WaitForTime(‘20:00:00’);
Encoders.StopAll;
Si può anche avviare o fermare un singolo encoder usando l’indice, che, ricordiamo, parte
da zero:
PAL.Loop := True;
PAL.WaitForTime(‘07:00:00’);
Encoders[0].Start;
PAL.WaitForTime(‘20:00:00’);
Encoders[0].Stop;
1.7.1.4.3. Archiviazione
Sin dalla versione 3.3.2 di SAM Broadcaster, è possibile che un encoder non faccia
streaming ma si occupi esclusivamente di archiviare l’audio prodotto dalla regia.
Lo script seguente permette a un encoder di frazionare la registrazione dell’audio in file
distinti tutti di durata oraria:
PAL.Loop := True;
PAL.WaitForTime(‘XX:00:00’);
Encoders[1].Stop;
Encoders[1].Start;
I valori 123 e ‘2005-01-01’ vanno inclusi all’interno della stringa. Per facilitare questa
procedura è possibile utilizzare dei marker che verranno valutati da PAL per costruire la
query.
Ad esempio si può scrivere:
var Q : TDataSet;
var s : string;
s:= ‘SELECT * FROM songlist WHERE (ID = :ID) AND (date_played < :DatePlayed)’
Q := Query(s,[123,’2005-01-01’],True);
Il tipo TDataSet rappresenta i risultati di una query SQL (resultset). La funzione query ha
3 parametri: il primo è la query, il secondo contiene tutti i parametri SQL, separati da
virgole all’interno di parentesi quadre, mentre il terzo è il parametro readonly. Il
parametro readonly, se impostato come true, non permetterà la modifica dei dati
estrapolati dalla query (quindi è bene impostarlo sempre a true a meno che non sia
necessaria una modifica, per evitare modifiche accidentali).
1.9.1.2. Comandi SQL
Il linguaggio PAL fornisce due comandi principali per manovrare il linguaggio SQL: sono
le funzioni Query e ExecSQL.
ExecSQL si usa per eseguire codice SQL che non restituisce dati. Questa funzione
restituisce il numero di righe che formavano la sottotabella della query:
function Query(Sql: String; Params: array di variabili; ReadOnly: Boolean): TDataSet;
Il comando Query viene invece usato con il comando SELECT, che ritorna un risultato.
Il risultato della query (che è a sua volta una tabella) viene incapsulato all’interno di un
oggetto TDataSet.
L’esempio seguente utilizza la funzione query per stampare i nomi degli ultimi 10 file
eseguiti. (la direttiva LIMIT non è supportata da Firebird):
var Q : TDataSet;
Q := Query(‘SELECT filename FROM songlist ORDER BY date_played DESC LIMIT 10’,[],True);
Q.First;
while not Q.EOF do
begin
WriteLn(Q[‘filenamè]);
Q.Next;
end;
Dopo avere estratto la sottotabella, la funzione First sposta il puntatore alla prima riga,
quindi finché la tabella non esaurisce tutte le righe, si estrae il nome file dalla riga e si va
avanti.
Lo script successivo seleziona tutti i brani che iniziano con la lettera A e ne setta il balance
a zero. Notare che la funzione query ha il parametro readonly settato su false.
var Q : TDataSet;
Q := Query(‘SELECT * FROM songlist WHERE title LIKE :title’,[‘A%’],False);
Q.First;
while not Q.EOF do
begin
WriteLn(Q[‘filename’]);
Q.Edit;
Q[‘balance’] := 0;
Q.Post;
Q.Next;
end;
La funzione edit permette di modificare la singola riga di un resultset, visto che di default
non lo è (meccanismo di sicurezza). La funzione post salva i dati all’interno del database
mentre la funzione next, come precedentemente detto, passa alla riga successiva.
1.9.2. Maneggiare i file
1.9.2.1. Funzioni per maneggiare i nomi dei file
Quelle di seguito elencate sono alcune funzioni che servono a maneggiare i nomi dei file.
EXTRACTFILEDIR
La stringa risultante è una directory che può essere passata come parametro per molte altre
funzioni, come CreateDir, GetCurrentDir, RemoveDir, o SetCurrentDir. La stringa è
vuota se il file non contiene parti che riconducano a directory.
EXTRACTFILEDRIVE
Questa funzione restituisce esclusivamente il drive (la lettera che corrisponde all’hard
disk, in ambiente Windows) del filename passato come parametro. I nomi che utilizzano la
convenzione UNC (Universal Naming Convention) vedranno restituito qualcosa simile a:
‘\nomeserver\nomecomputer’.
EXTRACTFILEEXT
La stringa risultante contiene l’intero percorso del file dato come parametro tranne il nome
e l’estensione.
EXTRACTFILENAME
Una funzione molto comoda permette di leggere un intero file e copiarlo in una stringa:
var Data : String = ‘‘;
const MyFile = ‘c:\MyTestFile.txt’;
if FileExists(MyFile) then
begin
Data := FileToStr(MyFile);
WriteLn(‘Il contenuto del file è:’);
WriteLn(Data);
end
else
WriteLn(Il file non esiste.’);
Data := Data + DateTimeToStr(Now) + #13#10;
if not StrToFile(MyFile,Data) then
La seconda parte dello script rimuove tutti i commenti dal file M3U:
T := 0;
while T < List.Count do
begin
if (Trim(List[T])=‘‘) or (List[T][1]=‘#’) then
begin
WriteLn(‘Cancellazione riga: ‘+List[T]);
List.Delete(T);
end
else
T := T + 1;
end;
La classe TStringList contiene metodi che possono essere molto utili per la gestione dei
file di configurazione:
const ConfigFile = ‘c:\Config.ini’;
var List : TStringList;
var T : Integer;
List := TStringList.Create;
if FileExists(ConfigFile) then
List.LoadFromFile(ConfigFile)
else
begin
WriteLn(‘Il file di configurazione non esiste, se ne creerà uno’);
List.Values[‘stazione’] := ‘FabrizioMondo.com’;
List.Values[‘website’] := ‘http://www.fabriziomondo.com’;
List.Values[‘genere’] := ‘Dance, Musica Classica’;
List.SaveToFile(ConfigFile);
end;
List.Free;
La funzione Values creerà i vari campi del file di configurazione, che avranno come nome
il contenuto del parametro di Values, mentre come valore quello impostato alla funzione.
Il file di configurazione avrà un contenuto di questo tipo:
stazione=NomeStazione
website=http://www.sitointernet.com
genere=Dance,Musica classica,Metal
Il ciclo all’interno del programma serve nel caso in cui la ritrasmissione venisse skippata
per mancanza di collegamento o perché la web radio da ritrasmettere è caduta. Questo
ciclo tenterà 20 volte, intervallando ogni tentativo con un brano, a ripristinare la
ritrasmissione, finché non arriva l’orario di ripresa della normale programmazione.
SAM non è in grado di riconoscere i mime types per decidere quale contenuto abbia un
flusso in ingresso. Quello che fa è semplicemente guardare al nome del file. Ogni flusso
che termina con “.ogg” è un flusso Ogg/Vorbis, mentre ogni URL con protocollo “mms” è
un flusso WMA.
Si crea la singolare situazione in cui un flusso WMA non viene accettato se indicato con
protocollo HTTP, mentre viene accettato se indicato con protocollo MMS. Tutto il resto
viene considerato flusso MP3.
1.9.4. Maneggiare le categorie
1.9.4.1. Aggiornamento tramite le cartelle
È possibile aggiornare il database di SAM Broadcaster in modo molto agevole, tramite
due funzioni: Rescan e Clear.
Lo script seguente ne mostra un esempio:
PAL.Loop := True;
DIR[‘c:\content\newmusic’].Rescan;
CAT[‘NewMusic’].Clear;
CAT[‘NewMusic’].AddDir(‘c:\content\newmusic’,False,ipBottom);
La funzione Rescan inserisce nella libreria dei brani tutti i file della cartella ‘newmusic’
che non erano precedentemente stati inseriti e rimuove quelli che non esistono più
fisicamente in cartella. È quindi un’operazione d’aggiornamento.
La funzione Clear svuota la categoria ‘NewMusic’ per poi riempirla nuovamente con il
comando AddDir.
1.9.4.2. Copiare categorie
Il seguente script copia i brani di una categoria all’interno di un’altra categoria cercando di
non arrecare pesantezza al sistema:
const Cat_From = ‘MyCat1’;
const Cat_To = ‘MyCat2’;
var Q : TDataSet;
var T : Integer;
Q := CAT[Cat_From].SongList;
CAT[Cat_To].Clear;
PAL.LockExecution;
Q.First;
while not Q.EOF do
begin
CAT[Cat_To].AddFile(Q[‘filename’],ipBottom);
Q.Next;
if (T mod 10) = 0 then PAL.UnlockExecution;
if (T mod 10) = 0 then PAL.LockExecution;
T:=T+1;
end;
PAL.UnlockExecution;
All’interno del blocco, dopo il primo if, è possibile mettere qualsiasi logica di
discriminazione dei brani; nel caso proposto si richiede che sia una canzone, ma
ovviamente questo potrebbe non essere sufficientemente discriminatorio (e quasi
sicuramente non lo è).
1.10. PAL coding guideliness
Le coding guideliness sono un insieme di convenzioni che permettono di standardizzare il
modo in cui viene impostato il codice. Anche se non sono necessarie per il funzionamento
del programma, risultano comode se si vuole ottenere del codice facile da leggere anche
dopo molto tempo dalla stesura. Diventano fondamentali quando più di una persona lavora
sullo stesso codice. Non è necessario che adottiate esattamente lo stile di codifica qui
descritto, ma vi esorto ad adottarne uno.
CONVENZIONI SUI NOMI E SULLE MAIUSCOLE
♦ Le parole chiave del linguaggio vanno scritte o tutte in maiuscolo o tutte in minuscolo
♦ i nomi devono essere il più possibile descrittivi. Sia i nomi delle variabili che quelli
delle funzioni che quelli dei tipi
♦ bisogna comunque cercare un compromesso fra un nome descrittivo e un nome troppo
lungo
♦ il nome dei tipi comincia con T
♦ se il nome di un tipo è costruito unendo più parole, ogni parola interna al nome deve
iniziare per lettera maiuscola (questa convenzione si chiama CamelCase ed è
PiùComodaDiQuello CheSembraVistoCheNonSiPossonoInserireSpaziNeiNomi)
♦ è bene scegliere verbi per dare nomi alle funzioni e sostantivi per dare nomi alle
variabili.
CONVENZIONI SULL’INDENTAZIONE
♦ È bene evitare che le righe siano lunghe più di 80 colonne, se è possibile si devono
spezzare le righe troppo lunghe
♦ è bene non utilizzare il TAB per indentare, gli spazi sono molto più affidabili e non
rischiano di apparire diversi cambiando editor
♦ l’indentazione aumenta DOPO i delimitatori di blocco.
♦ si va a capo PRIMA dei delimitatori di blocco:
// Questo è bene
while (LongExpression1 or LongExpression2) do
begin
// DoSomething
// DoSomethingElse;
end;
// Questo è male
while (LongExpression1 or LongExpression2) do begin
// DoSomething
// DoSomethingElse;
end;
// Questo è male
while (LongExpression1 or LongExpression2) do
begin
// DoSomething
// DoSomethingElse;
end;
♦ quando una riga lunga viene spezzata la parte a capo viene indentata:
MyValue :=
MyValue + (SomeVeryLongStatement / OtherLongStatement);
♦ anche se è possibile inserire più di un’istruzione in una singola riga è bene non farlo
♦ la struttura if-then deve stare sempre in due righe:
// Questo è male
if A < B then DoSomething;
// Questo è bene
if A < B then
DoSomething;
COMMENTI
I commenti sono fondamentali per rendere un codice leggibile. Un uso eccessivo dei
commenti può però “interrompere” il flusso di lettura del codice:
♦ è bene descrivere cosa fa una procedura subito PRIMA della sua dichiarazione, non
farlo dentro
♦ è bene descrivere cosa fa un blocco di codice subito PRIMA che il blocco cominci, non
farlo dentro
♦ bisogna ridurre al minimo i commenti riferiti alle singole istruzioni, riducono la
leggibilità del codice
♦ i commenti vanno mantenuti e aggiornati insieme al codice! Dei commenti che
descrivono una funzione che intanto è stata modificata sono una fonte di confusione e
bug subdoli
♦ spesso capita di commentare porzioni di codice non più utili che vengono comunque
“conservate” dentro lo script; è una pratica pericolosa che va ridotta al minimo e
giustificata
♦ se una porzione di codice necessita di troppi commenti probabilmente è scritta male; i
commenti devono commentare il codice, non sopperire alla sua illeggibilità.
2. Gli oggetti e i loro metodi
Questo capitolo tratta le varie unità presentate nella documentazione ufficiale in linea
fornita nelle versioni di SAM Broadcaster.
Sono presenti dieci unità settoriali più un’unità base.
I vari metodi, le varie classi e le varie routine sono presentate per come svolgono il loro
ruolo, in alcuni casi è indicato anche un esempio applicativo che semplifichi la
comprensione.
2.1. Unità base
All’interno dell’unità base sono presenti funzioni e procedure varie utili per la
programmazione.
Le stringhe del formato DateTime specificano la formattazione dei dati date-time, quando
vengono convertiti in stringhe.
Queste stringhe vengono passate ai metodi e alle procedure di formattazione, come ad
esempio la funzione FormatDateTime, ma sono anche usate per impostare variabili
globali.
Le stringhe del formato DateTime sono composte da specificatori che rappresentano
valori da inserire all’interno delle stringhe formattate. Alcuni specificatori, come “d”, si
limitano a formattare numeri o stringhe, mentre altri come “/” si riferiscono a stringhe
derivanti da variabili globali e dipendono dalla macchina utilizzata.
Nella tabella seguente, gli specificatori sono indicati in minuscolo. Il minuscolo o
maiuscolo è ignorato tranne che negli specificatori “am/pm” e “a/p”.
SPECIFICATORE RISULTATO
Mostra la data usando il format indicato nella variabile globale chiamata ShortDateFormat,
c seguita dall’orario nel format dato dalla variabile globale LongTimeFormat. L’orario non
viene mostrato se la variabile date-time indica esattamente mezzanotte
Mostra il giorno come un’abbreviazione (Lun-Dom) usando le stringhe fornite dalla variabile
ddd
globale ShortDayNames
Mostra il giorno con il nome completo (Lunedì-Domenica) usando le stringhe contenute nella
dddd
variabile globale LongDayNames
ddddd Mostra la data usando il formato dato dalla variabile globale ShortDateFormat
dddddd Mostra la data usando il formato dato dalla variabile globale LongDateFormat
Mostra il mese come un numero, senza lo zero iniziale. Se la “m” segue una “h” o una doppia
m
“h” tale “m” mostra i minuti e non il mese
Mostra il mese come un numero, senza lo zero iniziale. Se la “m” segue una “h” o una doppia
mm
“h” tale m mostra i minuti e non il mese
Mostra il mese come un’abbreviazione (Gen-Dic) usando le stringhe fornite dalla variabile
mmm
globale ShortMonthNames
Mostra il mese con il nome completo (Gennaio-Dicembre) usando le stringhe contenute nella
mmmm
variabile globale LongMonthNames
Utilizza l’orologio di 12 ore e mostra “am” per gli orari mattutini e “pm” per gli orari dopo
am/pm
mezzogiorno. Questo specificatore è case sensitive
Utilizza l’orologio di 12 ore e mostra “a” per gli orari mattutini e “p” per gli orari dopo
a/p
mezzogiorno. Questo specificatore è case sensitive
♦ Esempio
lo script chiede all’user di inserire un nuovo titolo e quindi aggiorna il nome del flusso
in streaming per tutti gli encoder.
Sintassi:
var S : String;
S := InputBox(‘Aggiornamento Streaming’,’Inserisci un nuovo nome, sei pagato per questo’,’’);
if S <> ‘’ then
begin
var Song : TSongInfo;
Song := TSongInfo.Create;
Song[‹artist›] := ‹›;
Song[‹title›] := S;
song[‹duration›] := ٠;
Encoders.SongChange(Song);
Song.Free;
end;
♦ Esempio
Risultato := LastDelimiter(‘.:’,’c:\nomefile.est’);
S deve contenere due o tre numeri, separati da un carattere contenuto nella variabile
globale DateSeparator.
L’ordine tra giorni, mesi e anni è determinato dalla variabile globale ShortDateFormat.
Se la data contiene solo due numeri, verrà considerata la data dell’anno corrente.
Per convertire i valori dell’anno compresi tra 0 e 99 viene utilizzata la variabile globale
TwoDigitYearCenturyWindow.
Se questa variabile è pari a 0, i valori compresi tra 0 e 99 si intendono nel secolo corrente.
Se invece la variabile è maggiore di 0, il suo valore è sottratto dall’anno corrente per
determinare il “pivot”.
Il pivot impone una regola semplice: gli anni su o dopo il pivot sono mantenuti all’interno
del secolo corrente, mentre gli anni prima del pivot sono spostati al secolo successivo. Ad
esempio:
ANNO
VARIABILE PIVOT MM/GG/03 MM/GG/50 MM/DD/68
CORRENTE
Esempio di applicazione:
var Present: DateTime;
var Year, Month, Day, Hour, Min, Sec, MSec: Integer;
Present:= Now;
DecodeDate(Present, Year, Month, Day);
WriteLn(Oggi è il ‘ + IntToStr(Day) + ‘ del mese di ‘ + IntToStr(Month) + ‘ dell’anno ‘ + IntToStr(Year));
DecodeTime(Present, Hour, Min, Sec, MSec);
WriteLn(‘Sono passati ‘ + IntToStr(Min) + ‘ minuti dalle ‘ + IntToStr(Hour));
METODI PROPRIETÀ
SongChange Format
Start Started
Stop Status
WriteScript StatusLong
CLASSE TEncoders
METODI PROPRIETÀ
SongChange Count
StartAll Encoders
StopAll Recording
WriteScript
♦ Esempio
L’esempio mostra come creare un’oggetto TSongInfo e aggiornare le informazioni
dello streaming dentro l’encoder. In particolare, l’esempio tratta di un regista con un
programma schedulato per le 14:00 e dalla durata di 30 minuti. Lo script aggiorna
manualmente le informazioni in modo che l’ascoltatore sappia che contenuti stia
attualmente ascoltando.
var Song : TSongInfo;
Song := TSongInfo.Create;
Song[‘artist’] := ‘Tizio’;
Song[‘title’] := ‘On the MIX’;
song[‘duration’] := 30*60*1000; {30 minuti}
{Aggiorna le informazioni alle 2 del pomeriggio}
PAL.WaitForTime(‘14:00:00’);
Encoders.SongChange(Song);
Song.Free;
♦ Esempio
In questo esempio ci sono 2 encoder, dove il primo Encoder[0] è un Windows Media
encoder mentre Encoder[1] è un encoder MP3.
Viene aggiornato soltanto il primo, cambiando il caption ogni ora per mostrare l’ora
corrente.
PAL.Loop := True;
PAL.WaitForTime(‘XX:00:00’);
Encoders[0].WriteScript(‘CAPTION’,TimeToStr(Now));
METODI PROPRIETÀ
Update Active
Alias
Bitrate
Details
Format
Host
ID
IsPrivate
Port
Server
Status
Url
Viewers
Viewers_High
Viewers_Max
CLASSE TRelays
METODI PROPRIETÀ
AtLeastOneActive Count
Update Relays
Viewers
Viewers_High
Viewers_Max
2.3.1.8. Proprietà ID
ID è una proprietà di sola lettura che restituisce l’identificativo attribuito al flusso da
AudioRealm.com, che però può variare senza preavviso e il cui utilizzo va quindi
sconsigliato se ci si aspetta un valore ben preciso.
Sintassi:
property ID: Integer;
Seek Status
Eject Volume
FadeToNext CanSeek
FadeToPlay Paused
FadeToPause Pitch
Next Tempo
Pause
Play
Stop
AGCLoadPresets
AGCSavePresets
♦ Esempio
var P : TPlayer;
var Song : TSongInfo;
PAL.Loop := True;
{## Wait for “on-the-hour”}
PAL.WaitForTime(‘XX:00:00’);
{## Detect the empty player and queue a station ID in it}
P := IdlePlayer;
if P <> nil then
begin
Song := CAT[‘Station IDs (All)’].ChooseSong(smRandom,NoRules);
if Song <> nil then P.QueueSong(Song);
{## Detect the active player and start the fade-out}
P := ActivePlayer;
if P <> nil then P.FadeToNext;
end;
Deck A Deck A
Deck B Deck B
Nessuno NULL
Sintassi:
function ActivePlayer: TPlayer;
2.4.1.29. Routine IdlePlayer
La routine IdlePlayer restituisce il player attualmente vuoto in regia secondo queste
regole:
Deck A Deck A
Deck B Deck B
Nessuno NULL
Sintassi:
function IdlePlayer: TPlayer;
Deck A Deck A
Deck B Deck B
Nessuno NULL
Sintassi:
function QueuedPlayer: TPlayer;
METODI PROPRIETÀ
IsEmpty Count
NextInQueue Duration
SongList ETA
Add
AddDir
AddFile
AddList
AddUrl
Clear
Delete
Shuffle
CLASSE TCategory
METODI PROPRIETÀ
ChooseSong Name
SongList
AddDir
AddFile
AddFiles
AddList
AddUrl
Clear
Delete
QueueBottom
QueueTop
Shuffle
Sort
CLASSE TDirCategory
METODI PROPRIETÀ
ChooseSong Path
QueueBottom SubDir
QueueTop
Rescan
CLASSE TCategoryTree
METODI PROPRIETÀ
- Categories
CLASSE TDirTree
METODI PROPRIETÀ
- DirCategories
ROUTINE DELL’UNITÀ
♦ Cat
♦ Dir
♦ Queue
♦ Subdir
♦ LoadPlaylist
♦ ExtractRandom
2.5.1. Classe TQueue
La classe TQueue controlla la coda.
2.5.1.1. Metodo IsEmpty
IsEmpty è una funzione booleana che restituisce true se la coda è vuota.
Sintassi:
function IsEmpty: Boolean;
CAMPO CONTENUTO
Sintassi:
function SongList: TDataSet;
♦ Esempio
PAL.Loop := True;
repeat
CAT[‘Station IDs (All)’].QueueBottom(smLemmingLogic,NoRules);
CAT[‘Music (All)’].QueueBottom(smWeighted,EnforceRules);
CAT[‘Music (All)’].QueueBottom(smLRP,EnforceRules);
until Queue.Count > 5;
{Wait until queue is empty}
repeat
PAL.WaitForPlayCount(1);
until Queue.IsEmpty;
{Start playing a random song}
var Song : TSongInfo;
var P : TPlayer;
P := IdlePlayer;
if P <> nil then
begin
Song := CAT[‘Tracks’].ChooseSong(smLemmingLogic,NoRules);
if Song <> nil then
begin
P.QueueSong(Song);
P.Play;
end;
end;
♦ Esempio
PAL.Loop := True;
{## Wait for midnight }
PAL.WaitForTime(‘23:59:59’);
{## Scan our promos directory }
DIR[‘c:\promos\’].Rescan;
{## Take a breather }
PAL.WaitForTime(‘+00:00:30’);
{## Scan our main music directory }
{## Include all subdirectories }
SUBDIR[‘c:\music\’].Rescan;
PROPRIETÀ
MinAlbumTime
MinArtistTime
MinTitleTime
MinTrackTime
MinQueueSize
UseGhostQueue
PROPRIETÀ
ArtistRule
AlbumRule
TitleRule
TrackRule
DailyLimit
DelayTime
Enabled
InsertPos
Limit
LimitInterval
CLASSE TRequestList
METODI
Queubottom
QueuTop
GetNextRequest
ROUTINE DELL’UNITÀ
♦ RequestPolicy
♦ Req
2.7.1. Classe TRequestPolicy
La classe TRequestPolicy controlla le regole inerenti le richieste degli ascoltatori.
2.7.1.1. Proprietà ArtistRule
ArtistRule è una proprietà booleana di lettura/scrittura che, se settata come true,
controllerà che ogni richiesta rispetti il minimo valore di attesa (come visto nel paragrafo
2.6.1.2) per trasmettere lo stesso artista.
Sintassi:
property ArtistRule: Boolean;
COSTRUTTORE PROPRIETÀ
Create Values
♦ Esempio
var Song : TSongInfo;
Song := TSongInfo.Create;
Song[‘artist’] := ‘Live DJ’;
Song[‘title’] := ‘Mixing live from Club 747’;
song[‘duration’] := 30*60*1000; {30 minutes}
{Update song information at 2pm}
PAL.WaitForTime(‘14:00:00’);
Encoders.SongChange(Song);
Song.Free;
METODI PROPRIETÀ
BOF Values
EOF
Append
Cancel
Edit
First
Insert
Delete
Previous
Last
Next
Post
ROUTINE DELL’UNITÀ
♦ ExecSQL
♦ Query
2.9.1. Classe Tdataset
La classe Tdataset è usata per accedere ai risultati di una query SQL.
2.9.1.1. Metodo BOF
BOF è una funzione booleana che restituisce true se il dataset è sul primo record del
database.
Sintassi:
function BOF: Boolean;
METODI PROPRIETÀ
Find Duplicates
Sort Sorted
COMPONENTE TIntegerHashTable
COSTRUTTORE METODI
Create Get
HasKey
RemoveKey
Put
COMPONENTE TStringHashTable
COSTRUTTORE METODI
Create Get
HasKey
RemoveKey
Put
CLASSE TList
Count
IndexOf
Remove
Clear
Delete
Insert
CLASSE TString
PROPRIETÀ
Add Count
AddObject Names
IndexOf Objects
IndexOfName Strings
IndexOfObject Text
AddStrings Values
Clear
Delete
Exchange
Insert
InsertObject
LoadFromFile
Move
SafeToFile
CommaText
CLASSE THashTable
METODI
Capacity
Size
CLASSE TStack
COSTRUTTORE METODI
Create Count
Peek
Pop
Push
CLASSE TQueue
COSTRUTTORE METODI
Create Count
Peek
Pop
Push
VALORE SIGNIFICATO
Restituisce un’eccezione EStringListError quando viene fatto il tentativo di inserimento della stringa
dupError
duplicata
Nell’esempio salva una lista di categorie e quindi la usa per aggiungere file alla coda.
var L : TList;
var C : TCategory;
var I : Integer;
L := TList.Create;
L.Add(CAT[‘Tracks’]);
L.Add(CAT[‘Music (All)’]);
L.Add(CAT[‘Station IDs (All)’]);
for I := 0 to L.Count-1 do
begin
C := L[I] as TCategory;
C.QueueBottom(smWeighted,EnforceRules);
end;
L.Free;
Sintassi:
property Strings[Index: Integer]: String; default;
PROPRIETÀ
TimeMask
TIPI DI VARIABILE
TDynVarArray
ROUTINE DELL’UNITÀ
♦ CopyFile
♦ T
♦ WriteLn
♦ WriteStr
2.11.1. Classe TTimeObj
La classe TTimeObj è usata per generare facilmente un tipo di dato TDateTime. Si
accede alla classe tramite la funzione T.
2.11.1.1. Proprietà TimeMask
La proprietà di sola lettura TimeMask viene usata per generare un dato di tipo DateTime.
La maschera può avere diversi formati.
Sintassi:
property TimeMask [Mask: String]: DateTime; default;
TEMPO RELATIVO
Formato: HH:MM:SS
La maschera deve essere in formato 24 ore e contenere tutti i campi (HH rappresenta le
ore, MM rappresenta i minuti e SS rappresenta i secondi).
♦ Esempio
T[‘14:00:00’]
Entrambi restituiscono la mezz’ora esatta successiva, se i minuti dell’ora attuale sono < 30
allora verrà allineata alla mezz’ora, mentre se i minuti dell’ora attuale sono > 30 allora
verrà allineata all’ora esatta successiva.
NEXTQUARTER
NEXT15
Entrambi restituiscono il quarto d’ora esatto successivo, in base al momento in cui si
calcola la funzione. Se ad esempio viene chiamata alle 14:08, NEXTQUARTER
rappresenterà le 14:15. Se la funzione viene chiamata alle 14:20, NEXTQUARTER varrà
14:30.
2.11.2. Tipo TdynVarArray
TdynVarArray è un array dinamico usato per applicare varie funzioni aventi in ingresso
tipi e parametri diversi tra loro.
Sintassi:
type TDynVarArray = Array of Variant;
Il parametro SrcFileName è il percorso del file che deve essere copiato. Il parametro è il
percorso del file dove il file sorgente verrà copiato. Se il parametro FailIfExists è
impostato come true, il metodo non funzionerà se il file di destinazione esiste già.
Restituisce true se il file viene copiato correttamente.
2.11.4. Metodo T
T accede all’oggetto TimeObj che è usato per generare tempi in modo semplice.
Sintassi:
function T: TTimeObj;
PAL.UnlockExecution;
Il parametro smWeighted indica che SAM sceglierà il brano con il balance più basso tra
quelli disponibili.
Quindi, modificando il balance manualmente, la scelta sarà più responsabile.
PAL.Loop := True;
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;
// Imposta tutti i balance a zero
ExecSQL(‘UPDATE songlist SET balance = 0’,[]);
{*******************************************************************************
Regola 1
*******************************************************************************}
var current_time : DateTime = now;
var three_hours_ago : DateTime = T[‘-03:00:00’];
{******************************************************************************
REGOLA 1: NESSUN BRANO PUO’ RIPETERSI PRIMA DI TRE ORE
Questa regola applica delle penalità ai brani trasmessi nelle ultime tre ore. Il brano più recentemente trasmesso ha la
penalizzazione maggiore. Un brano trasmesso 2 ore e mezza fa riceve una penalità minore. Ogni secondo che avvicina la
trasmissione di un brano alla terza ora successiva, fa diminuire la penalità di 9,25.
******************************************************************************}
Pal.LockExecution;
// SET PENALTY VARIABLES
scale := 100;
points := 100;
increments := 10800; // il numero di secondi in tre ore
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
// imposta i nuovi balance per tutti i brani trasmessi nelle ultime tre ore
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(‘Regola 1 completata.’);
{*******************************************************************************
FINE REGOLA 1
*******************************************************************************}
{*******************************************************************************
INIZIO REGOLA 2
*******************************************************************************}
var lastgenre : String;
{*******************************************************************************
Regola numero 2: Un genere differente per ogni brano trasmesso
Controlla il genere del brano attualmente trasmesso e penalizza tutti i brani dello stesso genere. Questa regola aumenta la
varietà, specialmente se la stazione è di tipo Top 40. Questa regola garantisce che non si ascoltino di seguito due canzoni
rap o pop. Chiaramente questa regola può funzionare soltanto se i brani sono taggati correttamente.
Questa regola funziona se SAM è in modalità “Ghost Queue”, perché lo script controlla soltanto il brano attualmente in
trasmissione e non la coda, e se la coda è piena, il brano aggiunto sarebbe solo l’ultimo della lista.
*******************************************************************************}
Pal.LockExecution;
penalty := 20;
// Estrapola il genere dell’ultimo brano trasmesso
D := Query(‘SELECT FIRST 1 genre FROM songlist ORDER BY date_played DESC’, [], True);
lastgenre := (D[‘genre’]);
D.Free;
// Penalizza tutti i brani con genere uguale.
ExecSQL(‘UPDATE songlist SET balance = balance + :penalty WHERE genre = :lastgenre’,[penalty, lastgenre]);
PAL.UnlockExecution;
WriteLn(‘Regola 2 completata.’);
{*******************************************************************************
Fine regola 2
*******************************************************************************}
{*******************************************************************************
Inizio regola 3
********************************************************************************
********************************************************************************
Regola 3: L’artista non si ripete prima di un’ora.
Tutti gli artisti trasmessi negli ultimi 60 minuti saranno penalizzati, ed allo stesso modo.
*******************************************************************************}
Pal.LockExecution;
penalty := 100;
//Recupera tutti gli artisti trasmessi nell’ultima ora.
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(‘Regola 3 completata.’);
{*******************************************************************************
Fine regola 3
********************************************************************************
{*******************************************************************************
Inizio regola 4
********************************************************************************
********************************************************************************
Regola 4: Controlla la coda
Questa regola controlla la lista della coda. Ogni brano che sia già stato accodato riceverà una penalità.
*******************************************************************************}
Pal.LockExecution;
penalty := 100;
//Recupera tutti i brani dalla lista della coda
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(‘Regola 4 completata.’);
{*******************************************************************************
Fine regola 4
********************************************************************************}
{*******************************************************************************
Inizio regola 5
********************************************************************************}
var x : Integer;
{*******************************************************************************
Regola 5: Controlla il numero di trasmissioni
Questa regola controlla quante volte ciascun brano è stato trasmesso. La regola premierà i brani non trasmessi e
penalizzerà i brani che sono andati in streaming più volte.
********************************************************************************}
PAL.LockExecution;
scale := 100;
points := 100;
// Recupera il numero più alto di trasmissioni di un brano
D := Query(‘SELECT FIRST 1 DISTINCT count_played from songlist ORDER BY count_played DESC’, [], 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(‘Regola 5 completata.’);
{*******************************************************************************
Fine regola 5
********************************************************************************}
{*******************************************************************************
Estrapolazione della miglior canzone
********************************************************************************
********************************************************************************
Questa parte dello script estrapola il brano con il balance minore. Se ci sono brani con lo stesso balance, verrà scelto uno
di loro casualmente.
******************************************************************************}
WriteLn(‘Scelta del miglior brano’);
Pal.LockExecution;
// Recupera i balance più bassi
D := Query(‘SELECT balance FROM songlist ORDER BY balance ASC’, [], True);
balance := D[‘balance’];
D.Free;
// Conta il numero di brani con questo balance
D:= Query(‘SELECT FIRST 1 filename, balance FROM songlist WHERE balance = :balance ORDER BY
RAND()’, [balance], True);
// Accoda il brano selezionato
Queue.AddFile(D[‘filename’],ipBottom);
WriteLn(‘Adding: ‘ + D[‘filename’] + ‘ to queue.’);
D.Free;
PAL.UnlockExecution;
{*******************************************************************************
Fine scelta miglior brano
*******************************************************************************}
// Attendi che venga trasmesso il brano prima di riprendere lo script.
PAL.WaitForPlayCount(1);
3.3. Come impostare regole differenti per le richieste e per i brani in coda
L’obiettivo dello script è quello di:
1. mantenere 2 differenti tipi di regole su come le canzoni richieste e gli artisti richiesti
possano ripetersi: ogni ora, se la canzone viene richiesta, più a lungo se il brano è
selezionato dallo script e non richiesto
2. dare maggiore priorità alle richieste che non alla selezione via script. Lo script non
selezionerà una canzone e non la aggiungerà alla fine della coda fin quando la durata
della coda stessa non sarà scesa sotto un’ora. Se le richieste arrivano a mantenere
costantemente la coda sopra la durata di un’ora, lo script non aggiungerà canzoni
3. assicurarsi che i brani non vengano ripetuti prima di un’ora
4. evitare che la richiesta venga rifiutata perchè il brano è stato recentemente trasmesso
5. cambiare il numero di richieste consentite in base al numero di ascoltatori connessi alla
radio
6. variare la selezione dei brani in modo casuale.
// Assicura la continuità di esecuzione dello script
PAL.Loop := True;
// Dichiara le variabili
// Imposta la durata della coda ad un’ora.
var QueueTime: Integer = (60*60*1000);
var categoria : String;
var criterio : Integer;
var minutiRipetizioneBrano : Integer;
var minutiRipetizioneArtista : Integer;
//=======================================
// Ricalcolo dinamico dei limiti sulle richieste
// Impostiamo inizialmente la proprietà limitInterval a 60 minuti.
RequestPolicy.LimitInterval := 60;
// Recupera il numero di ascoltatori correnti ed utilizza il valore
// per decidere quante richieste all’ora si possano autorizzare
PAL.LockExecution;
Case (Relays[0].Viewers) of
1: // 1 solo ascoltatore? 10 richieste all’ora a persona
begin
RequestPolicy.Limit := 10;
end;
2: // 2 ascoltatori, 8 richieste a persona
begin
RequestPolicy.Limit := 8;
end;
3: // 3 ascoltatori, 6 richieste a persona
begin
RequestPolicy.Limit := 6;
end;
4: // 4 ascoltatori, 4 richieste a persona
begin
RequestPolicy.Limit := 4;
end;
5..6: // 5 o 6 ascoltatori, 3 richieste a persona
begin
RequestPolicy.Limit := 3;
end;
7..9: // da 7 a 9 ascoltatori, 2 richieste a persona
begin
RequestPolicy.Limit := 2;
end;
Else // in tutti gli altri casi, 1 richiesta a persona
begin
RequestPolicy.Limit := 1;
end;
End;
PAL.UnlockExecution;
//=======================================
// Controlla la durata della coda attuale
// Ogni minuto, si controllerà la durata della coda. Se ad un controllo
// risulta esserci meno di un’ora di brani accodati, si aggiungerà un brano // alla fine della coda.
While Queue.Duration > QueueTime Do
Pal.WaitForTime(‘+00:01:00’);
//=======================================
// Come espresso in documentazione, i seguenti valori rappresentano le
// logiche di selezione dei brani
// smWeighted = 0 Brano con balance più basso
// smPriority = 1 Brano con maggiore priorità
// smRandom = 2 Selezione totalmente casuale
// smMRP = 3 Brano più recentemente trasmesso
// smLRP = 4 Brano meno recentemente trasmesso
// smMRPA = 5 Artista più recentemente trasmesso
// smLRPA = 6 Artista meno recentemente trasmesso
// smLemmingLogic = 7 Scelta di una logica casuale
// Lo script utilizza solo 5 di queste logiche, ma tale dato è configurabile.
// Non vengono utilizzate le logiche 2 e 7.
// Il blocco successivo sceglie una delle cinque logiche
// tramite un numero casuale e popola la variabile ‘criterio’
// con il metodo logico che è stato casualmente scelto.
//=======================================
// Sceglie un numero casuale da 1 a 20, e usa il numero casuale per
// selezionare la logica da utilizzare al momento
// della scelta di un brano da inserire in coda.
PAL.LockExecution;
Randomize;
// Seleziona un numero casuale
case (RandomInt(20) + 1) of
1..2:
begin
criterio := 0; // in questo caso, smWeighted
end;
3..4:
begin
criterio := 1; // in questo caso, smPriority
end;
5..9:
begin
criterio := 2; // in questo caso, smRandom
end;
10..17 :
begin
criterio := 4; // in questo caso, smLRP
end;
18..20 :
begin
criterio := 6; // in questo caso, smLRPA
end;
end;
// A questo punto, la variabile criterio contiene il numero che corrisponde // alla logica di selezione brani che andremo ad
usare quando si inserisce un // brano in coda.
//=======================================
// Determina da quale categoria estrarre un brano
// Genera un nuovo numero casuale.
// Usa il numero casuale per estrarre una categoria.
// Necessario sostituire le categorie qui espresse con quelle utilizzate nel // caso specifico
case (RandomInt(56) + 1) of
1..10 :
begin
categoria := ‘Alternative’;
end;
11..20 :
begin
categoria := ‘Metal’;
end;
21..30 :
begin
categoria := ‘70s’;
end;
31..40 :
begin
categoria := ‘80s’;
end;
41..48 :
begin
categoria := ‘60s’;
end;
49..54 :
begin
categoria := ‘Soul’;
end;
55..56 :
begin
categoria := ‘Punk’;
end;
end;
// A questo punto la variabile categoria contiene il nome della categoria dalla quale estrarre un brano
//=======================================
// Lo script imposterà i limiti temporali di ripetizione brani ed artisti per // un secondo, abbastanza per selezionare un
brano da accodare. Quindi si
// resetteranno le regole in base a quello che si desidera impostare per
// le richieste.
// Il brano inserito dallo script ha quindi regole più stringenti rispetto al // brano che è stato richiesto da un ascoltatore.
// Per migliorare la varietà nella selezione dei brani, lo script utilizzerà numeri casuali per determinare il valore da
utilizzare per le regole.
//=======================================
// Lo script utilizza un valore casuale per determinare quanto spesso
// ritrasmettere un artista
// Seleziona un numero casuale da 1 a 4
case (RandomInt(4) + 1) of
1..2 :
begin
minutiRipetizioneArtista := 300; // L’artista si ripete non prima di 5 ore
end;
3:
begin
minutiRipetizioneArtista := 600; // L’artista si ripete non prima di 10 ore
end;
4:
begin
minutiRipetizioneArtista := 900; // L’artista si ripete non prima di 15 ore
end;
end;
//=======================================
// Lo script utilizza un valore casuale per determinare quanto spesso
// ritrasmettere un brano
// Seleziona un numero casuale da 1 a 7
case (RandomInt(7) + 1) of
1:
begin
// Il tempo minimo di ripetizione del brano è uguale al tempo minimo di ripetizione dell’artista moltiplicato per 2
minutiRipetizioneBrano := (minutiRipetizioneArtista * 2);
end;
2:
begin
// Il tempo minimo di ripetizione del brano è uguale al tempo minimo di ripetizione dell’artista moltiplicato per 3
minutiRipetizioneBrano := (minutiRipetizioneArtista * 3);
end;
3:
begin
// Il tempo minimo di ripetizione del brano è uguale al tempo minimo di ripetizione dell’artista moltiplicato per 4
minutiRipetizioneBrano := (minutiRipetizioneArtista * 4);
end;
4:
begin
// Il tempo minimo di ripetizione del brano è uguale al tempo minimo di ripetizione dell’artista moltiplicato per 5
minutiRipetizioneBrano := (minutiRipetizioneArtista * 5);
end;
5:
begin
// Il tempo minimo di ripetizione del brano è uguale al tempo minimo di ripetizione dell’artista moltiplicato per 6
minutiRipetizioneBrano := (minutiRipetizioneArtista * 6);
end;
6:
begin
// Il tempo minimo di ripetizione del brano è uguale al tempo minimo di ripetizione dell’artista moltiplicato per 7
minutiRipetizioneBrano := (minutiRipetizioneArtista * 7);
end;
7:
begin
// Il tempo minimo di ripetizione del brano è uguale al tempo minimo di ripetizione dell’artista moltiplicato per 10
minutiRipetizioneBrano := (minutiRipetizioneArtista * 10);
end;
end;
//=======================================
// Lo script utilizzerà i dati delle variabili per impostare le regole
PlaylistRules.MinSongTime := minutiRipetizioneBrano ;
PlaylistRules.MinArtistTime := minutiRipetizioneArtista ;
//=======================================
// Aggiunge un brano alla fine della coda. Lo script selezionerà il brano
// seguendo la logica, la categoria e le regole precedentemente impostate
// tramite i numeri casuali.
CAT[categoria].QueueBottom(criterio,EnforceRules);
//=======================================
// Dopo avere inserito un brano, si resettano le regole in modo che tutte le richieste abbiano applicate le seguenti regole
// Le seguenti regole saranno applicate soltanto per le richieste!
PlaylistRules.MinArtistTime := 0; // # of minutes before Artist repeat
PlaylistRules.MinSongTime := 0; // # of minutes before Song repeat
// La coda di un’ora che viene sempre mantenuta servirà
// come regola di non ripetizione, per cui non è un errore
// impostare le regole di minutaggio a zero.
// Le richieste per artisti già in coda verranno rifiutate dal sistema di
// richieste. Con questi rifiuti che prevengono l’inserimento dello stesso
// artista nella stessa ora, non è necessario applicare nessuna regola
// ulteriore alle richieste, al contrario dell’inserimento brani fatto dallo // script che deve invece essere particolarmente
regolato.
PAL.UnlockExecution;
OldSong := PlaylistRules.MinSongTime;
PlaylistRules.MinArtistTime := 0;
PlaylistRules.MinSongTime := 10;
CAT[Category].QueueTop(smLemmingLogic,EnforceRules);
PlaylistRules.MinArtistTime := OldArtist;
PlaylistRules.MinSongTime := OldSong;
end;
PAL.Loop := True;
PAL.WaitForPlayCount(3);
PutSweeper(‘Station IDs (All)’);
3.5. Come correggere la durata di un brano che SAM riconosce come non valida
Per risolvere il problema della visualizzazione scorretta della durata di un brano, problema
che si verifica abbastanza spesso indicando durata – 00:00, questo script ricarica il brano
incriminato su un player ausiliare e recupera le informazioni da lì. Successivamente
inserisce i dati corretti in database per quel brano.
Effettuare quest’operazione su tutto il database richiede molto tempo e soprattutto un
player ausiliare costantemente libero.
var myQ : TDataSet;
var filLoc : String;
var Song1 : TSongInfo;
//seleziona i brani con durata inferiore a 0,1 secondi.
myQ := Query(‘SELECT * FROM songlist WHERE duration < :value’,[100], False);
myQ.First;
Song1 := TSongInfo.Create;
while not myQ.EOF do
begin
filLoc := myQ[‘filename’];
Song1[‘filename’] := filLoc;
Song1[‘artist’] := myQ[‘artist’];
Song1[‘title’] := myQ[‘title’];
Aux1.QueueSong(Song1);
ExecSQL(‘UPDATE songlist SET duration = :newduration where id = :songid’,[Aux1.duration, myQ[‘ID’]]);
Aux1.Eject;
myQ.Next;
end;
myQ.Free;
Song1.Free;
3.6. Come aggiornare il database se cambia il percorso assoluto dei brani a seguito di
uno spostamento fisico
Se i brani di una radio vengono spostati da un hard disk a un altro, mantenendo lo stesso
albero delle directory, questo script può aggiornare il database facilmente. Se ad esempio
si passa dall’hard disk con lettera C a quello con lettera F, lo script automatizza la
transazione. Bisogna stare MOLTO attenti nell’utilizzo di questo script. Per grandi
quantità di brani, potrebbe richiedere molte ore di lavoro.
PAL.Loop := False;
var
D : TDataSet;
var
songID, position: Integer;
var
old_filename, new_filename, old_drive, new_drive : String;
{ ********** configurazione ************************************************* }
old_drive := ‘X’; // Lettera del drive di partenza
new_drive := ‘Y’; // Lettera del drive di destinazione
{ **************************************************************************** }
// Recupera tutti gli ID ed i nomi file dal database.
D:=Query(‘SELECT ID, filename FROM songlist’, [], True);
Pal.LockExecution;
D.First;
While Not D.EOF Do
Begin
songID := D[‘ID’];
old_filename := D[‘filename’];
position := Pos(old_drive, old_filename);
IF position = 1 THEN
Begin
new_filename := old_filename;
SetCharAt(new_filename, 1, new_drive);
ExecSQL (‘UPDATE songlist set filename = :new_filename WHERE ID = :songID’, [new_filename, songID]);
WriteLn (‘Cambio ‘ + old_filename);
WriteLn (‘ in ‘ + new_filename);
End;
D.Next;
End;
Pal.UnlockExecution;
D.Free;
end;
3.12. Come trasmettere pubblicità, jingle o promo a volume più alto della musica
Alzare il volume di trasmissione per jingle, spot e promo consente di attirare
maggiormente l’attenzione dell’ascoltatore. Questo script consente di programmare la
regia per aumentare il volume solo con alcune tipologie di file audio.
const NORMALE = 200;
const ALTO = 250;
PAL.Loop := True;
var P : TPlayer;
var Song : TSongInfo;
var WaitForPlay : Boolean;
P := QueuedPlayer;
if P <> nil then
begin
WaitForPlay := True;
Song := P.GetSongInfo;
case Song[‘songtype’] of
‘S’ : P.Volume := NORMALE;
‘I’,’P’,’J’,’A’,’N’,’V’,’X’ : P.Volume := ALTO;
else P.Volume := NORMALE;
end;
end
else WaitForPlay := False;
while WaitForPlay do
begin
WaitForPlay := False;
PAL.WaitForPlayCount(1);
end;
PAL.WaitForTime(EndTime);
Queue.Clear;
ActivePlayer.FadeToNext;
end;
Seguimi su:
LinkedIn: fabriziomondo
Facebook: fabrizio.mondo.professionale
Twitter: FabrizioMondo
Vincenzo La Spesa
È un informatico palermitano. Scrive un blog di settore da oltre 8
anni. Programmatore versatile e appassionato di linguaggi di
programmazione, ne ha usati più di 15. Ha lavorato in diversi
settori dell’informatica, ma predilige l’ingegneria del software,
l’analisi dei dati e tutto ciò che riguarda l’Algoritmica. Quando
non si trova davanti uno schermo è un appassionato di arti
marziali, gioca a Go e prova ad emulare Dijkstra almeno nello
scrivere con la stilografica. Si vanta con i suoi colleghi informatici si avere ancora 10/10
di vista.
Seguimi su:
thedarshan.com
go.thedarshan.com
LinkedIn: vincenzolaspesa
Facebook: vincenzo.laspesa
Twitter: Darshan_tweet
Scribd: theDarshan