Sei sulla pagina 1di 163

CAMBIARE

CAPPELLO SIGNIFICA CAMBIARE IDEE,


AVERE UN’ALTRA VISIONE DEL MONDO.
C.G. Jung

Dario Flaccovio Editore


©
Fabrizio Mondo - Vincenzo La Spesa
PROGRAMMARE UNA WEB RADIO CON I PAL SCRIPT
© 2015 by Dario Flaccovio Editore s.r.l.
info@darioflaccovio.it www.darioflaccovio.it
www.facebook.com/DarioFlaccovioEditore
e-book ISBN 9788857905235
Prima edizione digitale novembre 2015
Edizione digitale realizzata da: Dario Flaccovio Editore s.r.l.
Questo e-book viene ceduto in licenza al solo acquirente. Tutto il materiale contenuto in
questo e-book è coperto da copyright. Sono vietati: copiatura, riproduzione, trasferimento,
noleggio, distribuzione, trasmissione in pubblico e utilizzo al di fuori di quanto previsto
dalla legge applicabile. Qualsiasi utilizzo non espressamente autorizzato dall’editore
costituisce violazione dei diritti dell’editore e dell’autore ed è sanzionabile sia in campo
civile che penale ai sensi della legge 633/1941 e successive modifiche.

Dario Flaccovio Editore srl


Viale Croce Rossa, 28 Palermo, Tel. +390916700686 Fax +39091525738
Se vuoi ricevere eventuali aggiornamenti su questo libro
o su titoli correlati, ti consiglio di iscriverti alla mailing list.
È sempre meglio tenerci in contatto!
Ringraziamenti
Fabrizio
Se c’è qualcuno che va ringraziato per primo per la nascita di questo libro è sicuramente
Spacial Audio, la società, incorporata da Triton Digital, che ha realizzato il software Sam
Broadcaster utilizzato da registi radiofonici in tutto il mondo.
Un grazie colossale va a Vincenzo, che è stato assolutamente fondamentale nella
realizzazione del testo, nel suo ordine e nella qualità di quanto abbiamo realizzato.
Un grazie anche a mia moglie Lavinia e mio figlio Tancredi, che nonostante i periodi di
difficoltà nella realizzazione di un testo che per il settore tecnico radiofonico non ha
precedenti, mi hanno sempre spronato ad andare avanti.
L’ultimo ringraziamento e forse il più importante va alla casa Editrice, con la quale esiste
un rapporto di grande stima e fiducia, in particolare ad Enrico, Alessia e Fabrizio, e per il
loro grande impegno nell’editoria.
Vincenzo
Vorrei innanzi tutto ringraziare Fabrizio per avermi coinvolto in questo progetto, e per
avermi convinto, malgrado gli altri impegni lavorativi, a partecipare alla stesura di
un’opera importante per un settore ancora poco trattato in Italia.
Ringrazio anche Monica per avermi incoraggiato, supportato e sopportato mentre scrivevo
e stavo davanti a uno schermo anche durante le ferie di Agosto.
Ringrazio infine la casa editrice Flaccovio. Anche se non l’ho mai vista “fisicamente” ho
sempre sentito il loro supporto telematico.
Premessa
Programmare una web radio con i PAL script è un testo rivolto a tutti coloro che
utilizzano la regia radiofonica SAM Broadcaster per la realizzazione di una web radio e
vogliono portare il proprio strumento di trasmissione ai massimi livelli di
personalizzazione. Con la guida alla programmazione della regia, la possibilità di avere
pieno controllo dei propri strumenti diventa una realtà.
Il testo, che analizza la struttura del linguaggio PAL derivante dal Delphi, propone un
percorso all’interno delle strutture e dei costrutti tipici del linguaggio stesso e analizza le
caratteristiche delle varie funzioni proposte anche dalla stessa casa produttrice del
software. Per potere migliorare l’esperienza e permettere al regista inesperto di
comprendere la potenza del linguaggio, sono presenti anche alcuni esempi di certo
interesse.
Programmare una web radio con i PAL script è un testo che non può mancare all’interno
di uno studio radiofonico che si basi su SAM Broadcaster, una fra le regie maggiormente
usate nel mondo delle web radio. Uno strumento completo, scritto da chi da anni lavora
con questa regia e un must have di indubbia utilità.
Introduzione alla regia radiofonica
SAM Broadcaster
Sam Broadcaster è una regia radiofonica realizzata da Spacial Audio e orientata
all’automazione di emittenti radiofoniche che abbiano un forte occhio alla
particolarizzazione del flusso audio, a prescindere da quale canale utilizzino per la
diffusione al pubblico.
Tra le caratteristiche più importanti di questo software di automazione abbiamo la
possibilità di potere effettuare una vera e propria programmazione in codice sorgente e
durante l’esecuzione della regia.
Questo testo non vuole insegnare come utilizzare una regia radiofonica o come diventare
un regista, ma semplicemente come programmare una delle regie radiofoniche più diffuse
nel mondo delle web radio in modo da personalizzare la propria stazione fino al minimo
dettaglio.
Per utilizzare al meglio questo manuale, si darà per scontato che il regista radiofonico
conosca la regia SAM Broadcaster, ne sappia manovrare i moduli e sappia configurare le
numerose opzioni, ricordando alcuni settaggi della regia solo all’occorrenza e soltanto se
assolutamente necessario alla trattazione.
Al momento dell’installazione della regia, il modulo relativo ai PAL Script si trova nel
desktop B.

Cliccando sul + si aprirà la finestra di inserimento dello script. Si potrà scegliere se


inserire un file contenente codice già predisposto oppure se iniziarne uno ex novo.
Per iniziare a scrivere un nuovo documento con codice PAL è sufficiente cliccare su ok.
La schermata avrà una nuova riga con i dati del file vuoto, che potremo quindi modificare.

Facendo doppio click sulla riga, si aprirà il PAL Scripting IDE, ovvero l’area di
programmazione interna alla regia.

All’interno dell’IDE, che ricordiamo essere l’acronimo per integrated development


environment, ovvero ambiente integrato di sviluppo, troviamo le seguenti opzioni:
FILE

Consente di aprire o salvare un file PAL.


EDIT

Consente le normali funzioni di modifica e ricerca all’interno dell’area di lavoro.


RUN

Consente di compilare ed eseguire il sorgente, anche linea per linea.


VIEW

Consente di scegliere le opzioni per il debugging, tra cui:


♦ : mostra i messaggi forniti dal compilatore al momento della
VIEW COMPILER MESSAGE

compilazione del sorgente


♦ VIEW OUTPUT: mostra i risultati dell’esecuzione del sorgente
♦ : mostra la linea di codice in esecuzione.
SHOW EXECUTION LINE

In quest’ambiente di lavoro si può quindi cominciare a lavorare per la realizzazione dei


programmi di utilità per la propria stazione.
1. Introduzione al linguaggio di programmazione PAL
PAL è l’acronimo di Playlist Automation Language ed è un linguaggio di programmazione
sviluppato per permettere ai proprietari delle emittenti radiofoniche pieno controllo sulla
rotazione musicale.
L’obiettivo è quello di permettere che qualsiasi sogno del regista venga risolto
programmando adeguatamente.
A oggi PAL è utilizzato da moltissimi registi radiofonici, ovviamente tutti tramite la regia
SAM Broadcaster, per automatizzare operazioni come il cambio di regia, notiziari,
schedulazione di jingle, pubblicità e download di contenuti da internet.
In questo testo miro a spiegarti rapidamente le basi dello scripting PAL fornendoti anche
alcuni esempi utili alla programmazione. Il PAL scripting potrà sembrare difficile a ogni
nuovo utilizzatore, ma i risultati che comporta ti faranno rapidamente superare le difficoltà
e aumentare la voglia di imparare.
Per i più tecnici, PAL Script è basato su un linguaggio di programmazione a oggetti
chiamato Delphi, che è lo stesso linguaggio utilizzato per scrivere la regia principale.
Delphi fu sviluppato e reso celebre dalla Borland, la stessa azienda che rese famoso il
Pascal nei personal computer.
In seguito Borland è stata acquistata da Embarcadero.
Il Delphi deriva dal Pascal, al quale ha aggiunto il paradigma di programmazione orientato
agli oggetti, molto comodo per gestire le varie parti della regia.
Vedremo, nei vari capitoli di questo testo, che ogni parte della regia, sia esso un encoder o
un deck oppure la collezione dei brani, per PAL rappresenta l’istanza di un oggetto.
Malgrado ciò il capitolo sarà improntato sulla programmazione procedurale. Ci limiteremo
quindi a usare gli oggetti in un paradigma più tradizionale che risulti più comodo per
sviluppare script.
Affronteremo prima la sintassi del linguaggio e infine analizzeremo la libreria di funzioni
che permette di controllare la regia.
1.1. Prima di cominciare: diamo un’occhiata all’IDE
Come da tradizione, il primo programma da realizzare quando si documenta un linguaggio
di programmazione è l’hello world, per fare questo scriviamo all’interno della finestra:
WriteLn(‘Hello world’);

Clicchiamo sul comando run.


Il risultato sarà quello mostrato in figura:
La linea blu mostra quale linea di codice sia in esecuzione, mentre il relativo output è
scritto sul lato destro dell’IDE. Nel caso del programma in questione, dovrebbe mostrare
“Hello World!”.
Hai creato il tuo primo PAL script funzionante. Chiaramente uno script di questo tipo non
serve a nulla, ma è pur sempre una soddisfazione.
Quello che abbiamo appena visto è l’IDE integrato in SAM Broadcaster.
Sarò sincero… non è il migliore degli IDE. Un’alternativa che può risultare comoda è
utilizzare un buon editor testuale per scrivere gli script e poi l’IDE per testarli.
Due buone scelte sono SublimeText e Notepad++.
1.1.1. Compilatore o interprete?
Malgrado ci sia in bella vista un tasto compila è bene ricordare che PAL è un linguaggio
interpretato.
Il tasto COMPILA non esegue una compilazione ma soltanto un’analisi statica del codice per
cercare errori.
Non genera nessun file oggetto, né bytecode, né tantomeno un eseguibile.
1.1.2. Prestazioni e threading
Come noterete molto presto i PAL vengono eseguiti a una velocità bassissima, circa
un’istruzione al secondo. Questa limitazione dipende dal fatto che vengono eseguiti
all’interno del thread principale della regia e quindi potrebbero rallentare l’intero
programma.
Vedremo in seguito come aggirare questo limite e vedremo anche che è pericoloso.
Se uno script si blocca, rischia di bloccare l’intera regia. Questo va tenuto sempre a mente
quando si eseguono operazioni lente e “bloccanti” come leggere il contenuto di una pagina
web da un server di cui non conosciamo la latenza.
Anche un minimo rallentamento può essere “sentito” dagli ascoltatori come un fastidioso
flickering nell’audio.
1.2. Il PAL script e la sua sintassi
Il PAL script è un sistema di scripting basato su DWScript, uno script-engine pensato per
permettere agli sviluppatori Delphi di utilizzare un linguaggio di scripting a loro familiare.
È infatti scritto in Delphi e la sua sintassi è un subset di Delphi stesso.
Dephi è a sua volta un’evoluzione a oggetti del Pascal sviluppata originariamente da
Borland.
1.2.1. Struttura generale di un programma PAL
Un programma PAL è composto da una serie di istruzioni e strutture di controllo:
♦ il blocco begin/end globale non è necessario
♦ non è possibile utilizzare i blocchi dichiarativi var, const e type nel main, le keyword
vanno quindi ripetute esplicitamente prima di ogni dichiarazione (è invece possibile
utilizzarle nelle funzioni).

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
}

Il Pascal prevedeva anche un altro tipo di commento multilinea delimitato da (* *).


QUESTO TIPO DI COMMENTO NON È SUPPORTATO DA PAL.

1.2.3. Variabili e letterali


Qualunque programma espresso in un linguaggio procedurale è composto da due entità
essenziali: una descrizione delle azioni che verranno intraprese e una descrizione dei dati
che verranno manipolati da queste istruzioni.
I dati sono rappresentati attraverso le variabili. Ogni variabile prima di poter essere
utilizzata necessita di essere dichiarata. La dichiarazione ne definisce il nome e ne
descrive il tipo.
Il PAL è un linguaggio a tipizzazione statica:
♦ le variabili vanno dichiarate e ne va dichiarato il tipo
♦ il tipo non può essere cambiato.
La dichiarazione di variabili avviene con la keyword var che ha la seguente struttura:
var nomeVariabile : NomeTipo;

Si possono dichiarare più variabili dello stesso tipo nella stessa istruzione:
var a,b,c:Integer;

Le variabili possono essere inizializzate in fase di dichiarazione:


var d:Integer = 1;

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

Integer Numero intero

Float Numero in virgola mobile (“real” non è definito invece)

Boolean Valore booleano (vero o falso)

Character Singolo carattere

String Sequenza di caratteri

TIPI COMPOSTI

Array Array e matrici

Record Strutture composte


Object Un oggetto

Set Variabili che possono assumere solo un insieme di valori

DateTime Descrive un timestamp (data e ora)

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’;

La manipolazione delle stringhe è fondamentale in PAL. Ce ne occuperemo


dettagliatamente nel prossimo capitolo.
BOOLEANI

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;

Non è necessario definire il tipo di una costante, viene dedotto dall’interprete.


const Val1 = ‘Questa è una stringa’;
const Val2 = 42;
const Val3 = 1.234;

const Val4 = False;

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.

Elenchiamo adesso gli operatori del PAL.


1.2.6.1. Operatori aritmetici
Per quanto riguarda gli operatori aritmetici, eccone un elenco con relativa descrizione:

OPERATORE E OPERANDI DESCRIZIONE

A + B Somma

A - B Sottrazione

A * B Prodotto

A / B Divisione in virgola mobile


A div B Divisione intera

A mod B Resto di divisione intera

A := B Assegnazione

1.2.6.2. Operatori di confronto


Gli operatori di confronto eseguono dei confronti fra i due operandi e ritornano sempre un
valore booleano:

OPERATORE E OPERANDI DESCRIZIONE

A = B Ritorna true se i valori sono uguali

A <> B Ritorna true se i valori sono diversi

A < B Ritorna true se A è minore di B

A > B Ritorna true se A è maggiore di B

A <= B Ritorna true se A è minore o uguale a B

A >= B Ritorna true se A è maggiore o uguale di B

A and B Ritorna true se A e B sono entrambi true

A or B Ritorna true se almeno uno fra A e B è true

not A Ritorna l’inverso del valore logico di A

1.2.7. Strutture di controllo


Le strutture di controllo del PAL possono essere suddivise in 3 tipologie:
♦ struttura sequenziale
♦ strutture condizionali
♦ strutture di iterazione.
Per sintetizzare visivamente i blocchi utilizzeremo dei diagrammi sintattici.
1.2.7.1. Struttura sequenziale
La struttura sequenziale è la struttura più semplice esistente, è quella che abbiamo
precedentemente chiamato blocco. Le istruzioni contenute in una struttura sequenziale
vengono eseguite in sequenza. Tutte le altre strutture di controllo possono ricevere una
struttura sequenziale.
Come abbiamo visto prima la struttura sequenziale è composta da una serie di istruzioni
racchiuse nel blocco begin/end. Il caso limite di una struttura sequenziale è la singola
istruzione, in questo caso non sono necessari i begin/end.
begin
istruzione1;
istruzione2
end;

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

Permette di eseguire uno dei due blocchi in base a una condizione.


Nella sua forma completa ha questa struttura:

if condizione then if condizione then


begin istruzione1
istruzione1; else
istruzione2; istruzione2
end ;
else
begin
istruzione3;
istruzione4;
end
;

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:

if condizione then if condizione then


begin istruzione1
istruzione1; ;
istruzione2;
end
;

Le tabulazioni e gli “a capo” sono tutti opzionali, ma aiutano a leggere il codice.


La struttura può essere riassunta da questo syntax diagram:

CASE-OF

L’istruzione case-of permette di eseguire una o più istruzioni in base al risultato di


un’espressione.
Si comporta come una sequenza di if che applicano diverse condizioni alla stessa
variabile.

var mese : integer;

case mese of
1 : Writeln( ‘Gennaio’ );
2 : Writeln( ‘Febbraio’ );
3 : Writeln( ‘Marzo’ );
4 : Writeln( ‘Aprile’ );
5 : Writeln( ‘Maggio’ );
6 : Writeln( ‘Giugno’ );
7 : Writeln( ‘Luglio’ );
8 : Writeln( ‘Agosto’ );
9 : Writeln( ‘Settembre’ );
10 : Writeln( ‘Ottobre’ );
11 : Writeln( ‘Novembre’ );
12 : Writeln( ‘Dicembre’ );
else
Writeln( ‘mese non corretto’ );
end;

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 syntax diagram dell’istruzione (un po’ complesso a dire il vero) è il seguente:

1.2.7.3. Strutture di iterazione


Le strutture di iterazione sono quelle di seguito descritte.
REPEAT-UNTIL

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

Non è necessario utilizzare il blocco begin/end per delimitare le istruzioni.


Il syntax diagram dell’istruzione è il seguente:

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;

Il syntax diagram dell’istruzione è il seguente:

FOR-TO-DO

La struttura For-To-Do permette di eseguire un blocco di istruzioni un numero prefissato


di volte utilizzando una variabile contatore.
var a:Integer;
for a:=0 to 10 do
begin
WriteLn(a);
end

Il syntax diagram dell’istruzione è il seguente:


1.2.8. Error handling, gestione delle eccezioni
Una funzione prende dei parametri in input, li elabora e usa questo input per calcolare un
valore di output.
Durante l’elaborazione qualcosa potrebbe non funzionare e l’output potrebbe non essere
calcolabile.
Ci sono due modi in cui una funzione si può comportare in questo caso:
♦ ritorna un output che ha un valore riconosciuto come non valido
♦ lancia un’eccezione.
La libreria di PAL adotta quasi sempre quest’ultimo approccio.
Un’eccezione è un “segnale” che avverte la procedura chiamante che la procedura
chiamata non è andata a buon fine. Se la chiamante non gestisce l’eccezione, essa si
propaga verso l’alto, se raggiunge la procedura base genera un errore che blocca lo script.
Quando si usano delle procedure che possono ritornare un’eccezione bisogna sempre
prevederne la gestione.
Le eccezioni rendono più semplice la manutenzione degli script in quanto permettono di
separare il codice per la gestione degli errori dal codice che esegue le operazioni.
La gestione delle eccezioni è un argomento avanzato e in questo testo è stata volutamente
semplificata omettendo alcune funzioni del linguaggio.
La struttura utilizzata per gestire le eccezioni è il try-except, che funziona in questo
modo:
Try
//codice da controllare
Except
//azioni da intraprendere se il codice ha generato un’eccezione
Finally
//azioni da intraprendere se il codice ha generato un’eccezione
end;

Il blocco Finally è opzionale.


Il syntax diagram (semplificato) per la gestione delle eccezioni è il seguente:

Vediamolo all’opera nella versione migliorata di un codice precedente:


var a:integer=0;
repeat
OkCancelDlg(‘Continuerò a scocciarti finché non immetterai un numero pari’);
try
a:=StrToInt(Inputbox(‘Immetti un numero’,’’,’’));
except
OkCancelDlg(‘La cosa che hai immesso non è un numero!’);
a:=0;
end;
until a mod 2 = 0;

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;

1.2.9. Programmazione procedurale


Nella programmazione procedurale il programma è costituito da una serie di funzioni. Lo
scopo della suddivisione in funzioni è quello di organizzare il codice e di poterne
riutilizzare parti che hanno una funzione indipendente. PAL distingue due tipi di
“subroutine”, le procedure e le funzioni:
♦ una funzione può prendere zero o più parametri in ingresso, può però restituire un solo
valore
♦ le procedure sono funzioni che non restituiscono alcun valore
♦ il valore di ritorno viene impostato in una variabile che ha lo stesso nome della
funzione
♦ è possibile dichiarare una procedura all’interno di un’altra procedura, in questo caso la
procedura interna sarà vista soltanto da quella esterna
♦ le procedure dispongono di un blocco var prima del begin, serve per dichiarare le
variabili interne alla procedura
♦ una procedura definisce un simbolo a cui è associato un blocco di codice
♦ una procedura viene invocata semplicemente con il suo nome.
Vediamo l’esempio di una dichiarazione di procedura:
procedure Hello;
var
a:Integer;
b:Integer;
c:Integer;
begin
Writeln(‘Hello’);
Writeln(‘Word’);
end;

E adesso di una dichiarazione di funzione:


//Calcolo di un numero dalla sequenza di fibonacci
function fibonacci(n:Integer): Integer;
begin
if (n <= 2) then
fibonacci := 1
else
fibonacci := fibonacci(n-1) + fibonacci(n-2);
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;

1.2.9.1. Passare per riferimento, passare per valore


PAL, di default, passa i parametri per valore. Questo significa che quando passiamo una
variabile a una funzione questa ne riceve una copia. Se il valore viene modificato
all’interno della funzione la modifica non si riflette all’esterno. Il passaggio per
riferimento invece permette di modificare il valore di un parametro dall’interno di una
funzione. Il passaggio per riferimento viene implementato attraverso la parola chiave var.
procedure per_valore(n:Integer);
begin
n := n *2 ;
end;
procedure per_riferimento(VAR n:Integer);
begin
n := n *2 ;
end;
var a:integer=2;
WriteLn(a); // 2
per_valore(a);
WriteLn(a); // 2
per_riferimento(a);
WriteLn(a); // 4

1.2.10. Array e tipi composti


Nei linguaggi di alto livello i dati sono delle entità astratte che possono rappresentare
strutture complesse.
Si possono definire nuovi tipi utilizzando la dichiarazione Type.
I tipi dichiarati possono poi essere utilizzati allo stesso modo dei tipi nativi del linguaggio.
1.2.10.1. Array
In molti problemi si ha la necessità di aggregare molti dati semplici per facilitarne la
rappresentazione e rendere più veloce il loro ritrovamento.
Gli array permettono di definire “tabelle” di dati dello stesso tipo a cui si accede in base a
un indice.
Non ci sono limiti alle dimensioni che può assumere un array, che può essere
monodimensionale (vettore), bidimensionale (matrice) o multidimensionale.
Un array può essere utilizzato direttamente oppure si può definire un nuovo tipo a partire
da un array.
Vediamo qualche esempio:
var VettoreDaDieci : Array [0..10] of Float;

In questo caso abbiamo direttamente dichiarato una variabile. Equivalentemente potevamo


definire prima un tipo e poi utilizzarlo:
Type TipoVettoreDaDieci = Array [0..10] of Float;
Var VettoreDaDieci : TipoVettoreDaDieci;

La dimensione dell’array è impostata attraverso un intervallo.


ARRAY MULTIDIMENSIONALI

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;

var ArrayTridimensionaleDaDieci : Array [0..10,0..10,0..10] of Float;


ARRAY DINAMICI

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;

Per dimensionare un array dinamico si usa la funzione SetLenght:


SetLength(NomeVariabile, Dimensione);

È possibile conoscere l’attuale dimensione di un array con la funzione Length.


1.2.11. Tipi composti
Spesso una singola entità necessita di dati diversi per essere descritta. I tipi composti
permettono di definire un oggetto composto da campi eterogenei:
type
TAnagrafica = record
nome, cognome : string;
indirizzi: Array [1..3] of String;
telefono: String;
DataDiNascita: DateTime;
end;

I singoli campi di un tipo composto sono accessibili attraverso l’operatore punto:


var a:TAnagrafica;
a.nome := ‘Dylan’;
a.cognome := ‘Dog’;

a.indirizzi[1] := ‘Craven Road, 7’;

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 Sin(a : Float):Float; Seno

function Sinh(a : Float):Float; Seno iperbolico

function Cos(a : Float):Float; Coseno

function Cosh(a : Float):Float; Coseno iperbolico

function Tan(a : Float):Float; Tangente

function Tanh(a : Float):Float; Tangente iperbolica

function ArcSin(a : Float):Float; Arcoseno

function ArcSinh(a : Float):Float; Arcoseno iperbolico

function ArcCos(a : Float):Float; Arcocoseno

function ArcCosh(a : Float):Float; Arcocoseno iperbolico

function ArcTan(a : Float):Float; Arcotangente


function ArcTanh(a : Float):Float; Arcotangente iperbolica

function Cotan(a : Float):Float; Cotangente

function Hypot(x : Float; y : Float):Float; Ipotenusa

function Inc(var a : Integer; b : Integer):Float; Somma due interi, converte il risultato in float

function Abs(a : Float):Float; Valore assoluto

function Exp(a : Float):Float; Potenza applicata al numero di Nepero

function Ln(a : Float):Float; Logaritmo neperiano

function Log2(a : Float):Float; Logaritmo in base 2

function Log10(a : Float):Float; Logaritmo in base 10

function LogN(n : Float; x : Float):Float; Logaritmo in base arbitraria

function Sqr(v : Float):Float; Radice quadrata

function Int(v : Float):Float; Parte intera di un float (sempre come float)

function Frac(v : Float):Float; Parte frazionaria di un float (il numero meno la sua parte intera)

function Trunc(v : Float):Float; Arrotonda per troncamento

function Round(v : Float):Float; Arrotonda

function Power(base : Float; exponent : Float):Float; Potenza di un numero

function DegToRad(v : Float):Float; Gradi a radianti

function RadToDeg(v : Float):Float; Radianti a gradi

function Max(v1 : Float; v2 : Float):Float; Massimo fra due numeri

function Min(v1 : Float; v2 : Float):Float; Minimo fra due numeri

function Pi:Float; Pi greco (è una costante)

function Random:Float; Numero casuale float fra 0 e 1 (estremi esclusi)

function RandomInt(Range : Integer):Integer; Numero intero casuale fra 0 e un massimo specificato

1.3.2. Gestione delle stringhe


Il tipo stringa rappresenta una sequenza di caratteri codificati secondo lo standard ANSI.
Le stringhe sono onnipresenti negli script e PAL mette a disposizione molte funzioni per
gestirle.
Elenchiamo adesso le più utili funzioni per le stringhe:

function IntToStr(v : Integer):String; Da intero a stringa

function StrToInt(str : String):Integer; Da stringa a intero

function StrToIntDef(str : String; Def :


Da stringa a intero se è possibile, altrimenti ritorna def
Integer):Integer;

function IntToHex(v : Integer; Digits : Da intero a esadecimale col numero di cifre specificate (ritorna
Integer):String; una stringa)

function FloatToStr(v : Float):String; Da float a stringa

function StrToFloat(str : String):Float; Da stringa a float

function StrToFloatDef(str : String;


Da stringa a float con valore di default
Def :Float):Float;

function Chr(x : Integer):String; Carattere corrispondente al codice x

function CharAt(s : String; x : Integer):String; carattere alla posizione x nella stringa s

procedure SetCharAt(var S : String; x : Integer; c :


Imposta a S il carattere alla posizione index nella stringa c
String);

procedure Delete(var S : String; Index : Integer;


Elimina caratteri da una stringa
Len : Integer);

procedure Insert(src : String; var Dest : String;


Inserisce caratteri in una stringa
Index : Integer);

function LowerCase(Str : String):String; Rende minuscola una stringa

function AnsiLowerCase(Str : String):String; Rende minuscola una stringa ANSI

function UpperCase(Str : String):String; Rende maiuscola una stringa

function AnsiUpperCase(Str : String):String; Rende maiuscola una stringa ANSI

function Pos(SubStr : String; Str : string):Integer; Posizione di una sottostringa dentro la stringa

function Length(Str : String):Integer; Lunghezza di una stringa

function TrimLeft(Str : String):String; Elimina gli spazi alla fine della stringa

function TrimRight(Str : String):String; Elimina gli spazi all’inizio 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

function AnsiCompareText(Str1 : String; Str2 :


Confronta due stringhe ANSI
String):Integer;

function CompareStr(Str1 : String; Str2 :


Confronta due stringhe
String):Integer;

function AnsiCompareStr(Str1 : String; Str2 :


Confronta due stringhe ANSI
String):Integer;

function QuotedStr(Str : String):String; Quota una stringa (applica i caratteri di escape)

Riassumiamo con del codice. Un codice vale più di mille parole:


Var S1 : String = ‘Questa è una stringa’;
Var S2 : String = ‘Altra stringa’;
Var I : Integer;
S2 := S1;
S2 := S1 + ‘ e che stringa…’;
S2 := Copy(S1,2,3); //Copia solo alcuni caratteri di una stringa in un altra. (Copia 3 caratteri, partendo dalla posizione 2
WriteLn(S2);
I := Length(S2); {Restituisce la lunghezza di S2}
I := Pos(‘st’,S1); {Restituisce la posizione della prima occorrenza di “st”}

La funzione Copy accetta tre parametri, il primo è la stringa da copiare, il secondo il


carattere da cui iniziare a copiare, il terzo il numero di caratteri da copiare. Dopo
l’esecuzione di questa funzione, in S2 sarà presente la stringa “his”.
La funzione Pos accetta come primo parametro una stringa, detta pattern, e come secondo
parametro una variabile contenente una stringa. Restituisce l’indice della prima occorrenza
del pattern nella stringa contenitore.
1.4. Gestione del tempo
Le stazioni radiofoniche gestiscono quotidianamente i tempi. PAL fornisce molte opzioni
per la gestione e manipolazione del tempo.
Il tipo che rappresenta il tempo in PAL è DateTime.
È possibile trattare le date convertendole in float.
Per fare questo si costruisce un float che rappresenta la differenza in giorni fra la data da
convertire e il 30 dicembre 1899.
Per comprendere:
0 corrisponde a 30/12/1899 alle ore 12:00
2.75 corrisponde a 01/01/1900 alle ore 18:00
-1.25 corrisponde a 29/12/1899 alle ore 6:00
35065 corrisponde a 01/01/1996 alle ore 12:00
Ad esempio:
var D:Datetime;
D := Now; {data e orario correnti}
D := Date; {Oggi, a mezzogiorno}
D := Time; {Soltanto l’orario }
D := Now + (-1); {Ieri, allo stesso orario}
D := Now + (1.0/24); {tra un’ora}

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

T[‘+00:00:30’] crea un datetime pari a 30 secondi a partire da adesso.


T[‘-01:00:00’] genera un datetime pari a un’ora fa.
PROSSIMO ORARIO (WILDCARDS): XX: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)

♦ NEXTHOUR oppure NEXT60


Entrambi i comandi restituiscono l’ora successiva, esattamente come nel caso di
XX:00:00
♦ NEXTHALFHOUR oppure NEXT30
Entrambi i comandi restituiscono la mezz’ora successiva
♦ NEXTQUARTER oppure NEXT15
Entrambi i comandi restituiscono il quarto d’ora successivo.

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

La funzione DayOfWeek permette di estrarre il giorno della settimana da una data. I


giorni vengono espressi dalle costanti: Sunday = 1; Monday = 2; Thursday = 3;
Wednesday = 4; Tuesday = 5; Friday = 6; Saturday = 7.
if DayOfWeek(Now) = Sunday then writeln(‘E’ domenica!’);

Elenchiamo adesso le principali funzioni per la gestione del tempo in PAL.

function Now: DateTime; Ritorna un DateTime che rappresenta il momento corrente

function Date: DateTime; Ritorna un DateTime che rappresenta la data corrente

function Time: DateTime; Ritorna un DateTime che rappresenta l’ora corrente

function DateTimeToStr(dt: DateTime):string; Converte un DateTime in una stringa


function StrToDateTime(str : String): DateTime; Parsa un oggetto DateTime a partire da una stringa

function DateToStr(Date: DateTime):String; Converte un DateTime in una stringa (solo la data)

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)

Parsa un oggetto DateTime a partire da una stringa (solo il


function StrToTime(Str : String): DateTime;
tempo)

function DayOfWeek(dt : DateTime):Integer; Estrae il giorno dalla settimana da un DateTime

function FormatDateTime(Format : String; dt : Genera una stringa a partire da un DateTime e da una stringa di
DateTime):String; formato

function IsLeapYear(Year : Integer):Boolean; Ritorna true se l’anno è bisestile

function IncMonth(dt : DateTime; nm :


Incrementa il mese in un oggetto DateTime
Integer):DateTime;

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

function EncodeTime(hh, mm, ss, ms :


Costruisce un oggetto DateTime a partire dalle sue componenti
Integer):DateTime;

1.5. Comandi di attesa asincroni


I comandi di attesa asincrona permettono di interrompere l’esecuzione dello script (senza
bloccare SAM) in attesa di un evento.
I comandi di attesa sono 3 metodi inseriti dentro l’oggetto PAL:
PAL.WaitForPlayCount(Count);
PAL.WaitForQueue(Count);
PAL.WaitForTime(Time);

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

WaitForTime permette di interrompere l’esecuzione dello script per un tempo


determinato. Il parametro Time può essere costruito direttamente attraverso un DateTime:
var D : DateTime;
D := Now + 1; {Tomorrow this time}
PAL.WaitForTime(D);

Oppure attraverso la funzione una mask T:


PAL.WaitForTime(T[‘14:00:00’]);

Oppure attraverso una stringa:


PAL.WaitForTime(‘+00:00:10’); //Aspetta 10 secondi
PAL.WaitForTime(‘14:30:00’); //Aspetta le 14:30 di oggi
PAL.WaitForTime(‘XX:00:00’); //Aspetta la prossima ora
PAL.WaitForTime(T[‘14:00:00’]+1); //Aspetta le 15:00

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

Una spiacevole lacuna nell’implementazione delle funzioni asincrone è che:


NON È POSSIBILE ASPETTARE ALL’INTERNO DI COSTRUTTI IF,
NÉ ALL’INTERNO DI COSTRUTTI CASE, NÉ ALL’INTERNO DI FUNZIONI
E PROCEDURE DEFINITE DALL’UTENTE
Per aggirare il problema si può ad esempio ripetere il blocco di istruzioni dove necessario,
mentre al posto di if o case si può utilizzare il while-do.
Se per esempio volessimo realizzare uno script che fa una cosa del tipo “Se A è maggiore
di B, allora aspetta un brano”.
La prima implementazione che ci verrebbe in mente sarebbe:
if (A > B) then
begin
PAL.WaitForPlayCount(1);

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

1.7.1. I principali oggetti


Lo stato della regia può essere descritto da quattro componenti:
♦ la collezione dei brani
♦ la logica di selezione dei brani e le regole di playlist
♦ il motore audio che comprende i player di ogni tipo
♦ encoder e relay.
Ognuna di queste parti viene vista da PAL tramite oggetti che permettono allo script di
comandare la regia.
1.7.1.1. Collezione dei brani
SAM salva tutto quello che viene suonato all’interno della libreria dei brani. Più
precisamente SAM può suonare (e quindi trasmettere) solo brani presenti in libreria.
Questo, che a prima vista può apparire una limitazione, permette a SAM di gestire la
playlist e le logiche di selezione che caratterizzano un’emittente professionale.
1.7.1.1.1. Importazione di file nella libreria di brani
I seguenti comandi aggiungono brani all’interno della categoria identificata da CAT:
CAT[‘Categoria’].AddDir(‘c:\music\’, ipBottom);
//Aggiunge una directory
CAT[‘Categoria’].AddFile(‘c:\music\test.mp3’, ipBottom);
//Aggiunge un file
CAT[‘Categoria’].AddList(‘c:\music\playlist.m3u’, ipBottom)
//Aggiunge una playlist
CAT[‘MyCategory’].AddURL(‘http://localhost:8000/test.mp3’, ipBottom);//Aggiunge un URL

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

1.7.1.1.2. Esportazione di file dalla libreria di brani


È possibile salvare il contenuto di una categoria come file di testo per usi esterni a SAM
Broadcaster. I comandi principali, che valgono sia per le categorie che per la coda sono
SaveAsM3U e SaveAsCSV.
Il primo comando salva in formato M3U, mentre il secondo in CSV:
CAT[‘MyCategory’].SaveAsM3U(‘c:\list.m3u’);
//Esporta come m3u playlist
CAT[‘MyCategory’].SaveAsCSV(‘c:\list.csv’);
//Esporta come CSV data file
Queue.SaveAsM3U(‘c:\list.m3u’);
//Esporta come m3u playlist
Queue.SaveAsCSV(‘c:\list.csv’);
//Esporta come CSV data file

1.7.1.1.3. Uso della libreria per la rotazione oraria


La maggior parte dei registi conosce l’importanza di un’accurata classificazione dei brani.
Una volta inserito per ogni brano l’artista, il titolo, l’album di riferimento e altre
informazioni, la gestione dei brani stessi diventa molto facile, specialmente se i brani, gli
advertisement e i promo sono correttamente categorizzati.
Uno script come il seguente permette di gestire la coda e inserire i brani seguendo una
schedulazione scelta dal regista:
PAL.Loop := True;
Cat[‘Tracce’].QueueBottom(smLemmingLogic, EnforceRules);
Cat[‘Music (All)’].QueueBottom(smLRPA, EnforceRules);
Cat[‘Hits’].QueueBottom(smLRP, EnforceRules);
Cat[‘Pubblicita’].QueueBottom(smLRPA, NoRules);
PAL.WaitForPlayCount(4);

Lo script, che si ripeterà indefinitamente, inserisce 4 brani in coda e i brani sono in


sequenza: un brano della categoria “tracce”, un brano della categoria “musica”, un brano
della categoria “hits” e un brano della categoria “pubblicità”.
Il metodo QueueBottom accetta due parametri, il primo indica la modalità di rotazione,
definita da questa tabella:

NUMERO
NOME DEL PARAMETRO OPERAZIONE EFFETTUATA
EQUIVALENTE

smWeighted 0 Brano con il maggior peso

smPriority 1 Brano con maggior priorità

smRandom 2 Brano scelto casualmente

smMRP 3 Brano più recentemente suonato

smLRP 4 Brano meno recentemente suonato

smMRPA 5 Artista più recentemente suonato

smLRPA 6 Artista meno recentemente suonato

smLemmingLogic 7 Brano casuale secondo logiche scelte

Il secondo parametro indica se rispettare (EnforceRules) o no (NoRules) le regole


previste da Playlist Rotation Rules, quali ad esempio “non suonare lo stesso artista entro
XX minuti”.
Per la gestione della logica di creazione della playlist, si può usare anche più di un PAL
script per volta. Un caso didattico è quello di due script, dove il primo gestisce la musica e
il secondo jingle e pubblicità.
//Script PAL numero 1
PAL.Loop := True;
Cat[‘Tracks’].QueueBottom(smLemmingLogic, EnforceRules);
Cat[‘Music (All)’].QueueBottom(smLRPA, EnforceRules);
Cat[‘Hot Hits’].QueueBottom(smLRP, EnforceRules);

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;

TSonginfo è la classe contenente le informazioni di un brano. Il metodo GetSongInfo


estrapola da tale oggetto le sue variabili, che vengono stampate a video tramite i comandi
WriteLn.
1.7.1.3.2. Funzioni di utilità
ActivePlayer è l’oggetto che rappresenta il player considerato in quel momento attivo. Se
solo un player è attivo al momento dell’esecuzione della funzione, quel player sarà
restituito da questa funzione. Se entrambi i deck sono attivi, il player col maggior tempo di
attività rimanente sarà considerato attivo. Se nessuno dei due player è attivo, verrà
restituito nil.
IdlePlayer è l’oggetto che rappresenta quel player che non ha canzoni in coda e che non è
attivo in quel momento. Se tutti e due i player sono inattivi, viene di default restituito il
Deck A. Se entrambi sono attivi, verrà restituito nil.
QueuedPlayer è l’oggetto che rappresenta il player in modalità queued. La modalità
queued si ha quando il player ha un brano caricato sul deck ma non sta suonando nessuna
traccia. Se entrambi i deck sono attivi o inattivi, la funzione ritorna nil, altrimenti
restituisce il primo dei deck in queued.
1.7.1.4. Encoders e relay di statistiche
PAL permette l’accesso agli encoder e ai relay di statistiche.
Questa possibilità fornisce la capacità di fermare o azionare gli encoder, così come di
inserire dati all’interno dei flussi.
1.7.1.4.1. Relay di statistiche
La classe relativa ai relay permette di fare cinque azioni principali:
1. leggere lo stato di tutti i relay
2. leggere il numero di ascoltatori correnti
3. leggere il picco di ascoltatori
4. leggere il numero di slot massimi disponibili
5. forzare l’aggiornamento.
SCRIPT D’ESEMPIO N. 1 - STAMPA TUTTI I DATI SOMMATI
PAL.LockExecution;
WriteLn(‘Panoramica dei relay ‘);
WriteStr(‘—Attività: ‘);
if Relays.AtLeastOneActive then WriteLn(‘Online’) else WriteLn(‘Offline’);
WriteStr(‘—Ascoltatori: ‘); WriteLn(Relays.Viewers);
WriteStr(‘—Picco: ‘); WriteLn(Relays.Viewers_High);
WriteStr(‘—Max: ‘); WriteLn(Relays.Viewers_Max);
WriteLn(‘‘);
PAL.UnlockExecution;
SCRIPT D’ESEMPIO N. 2 - STAMPA I DATI SINGOLARMENTE
var I : Integer;
PAL.LockExecution;
for I := 0 to Relays.Count-1 do
begin
WriteStr(‘Relay numero ‘);
WriteLn(I);
WriteStr(‘—Attività: ‘);
WriteLn(Relays[I].Active);
WriteStr(‘—Stato: ‘);
WriteLn(Relays[I].Status);
WriteStr(‘—Ascoltatori: ‘);
WriteLn(Relays[I].Viewers);
WriteStr(‘—Picco: ‘);
WriteLn(Relays[I].Viewers_High);
WriteStr(‘—Bitrate (kbps): ‘);
WriteLn(Relays[I].Bitrate);
WriteStr(‘—Formato: ‘);
WriteLn(Relays[I].Format);
WriteLn(‘‘);
end;
PAL.UnlockExecution;

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;

1.8. Cambiare regista agevolmente


Moltissime stazioni hanno più registi, spesso localizzati in posizioni geograficamente
parecchio distanti tra loro. Lo script seguente mostra una metodologia di scambio tra
registi a livello di server shoutcast.
Il regista che desidera effettuare la sua diretta, semplicemente rimuove la sorgente
precedente e avvia la sua trasmissione.
{
Utilizzo dello script:
a) Create un encoder mp3 per la connessione al server shoutcast.
b) Inserite I dati all’interno della configurazione encoder
c) Utilizzate l’event scheduler far partire lo script al corretto orario.
}
{ Configurazione del server shoutcast }
{==================================================}
const shoutcast_password = ‘changeme’;
const shoutcast_host = ‘localhost’;
const shoutcast_port = ‘8000’;
{==================================================}
{ Implementazione }
{––––––––––––––––—}
{ Costruzione dell’indirizzo per l’invio del commando al server shoutcast }
var URL : String;
URL := ‘http://admin:’+shoutcast_password+’@’+shoutcast_host+’:’+shoutcast_port+’/admin.cgi?mode=kicksrc’;
{ Rimuove la sorgente dal server shoutcast }
WebToFile(‘c:\dummy.txt’,URL);
{ Avvio degli encoder }
{ NOTA: Si suppone ci sia un solo encoder}
Encoders.StartAll;
{––––––––––––––––—}

1.9. Tecniche di scripting avanzate


1.9.1. Maneggiare il database
Il linguaggio PAL permette di accedere direttamente al database tramite query SQL.
Bisogna stare più che attenti nel lavorare direttamente con il database perché, sebbene
possa sembrare molto comodo, è anche molto facile commettere errori che possano
compromettere dati o farli perdere.
Il consiglio non è di certo quello di non lavorare con il database, ma quello di effettuare
backup frequenti.
Per comprendere al meglio questa sezione del linguaggio PAL, occorre avere delle (solide)
basi di SQL, linguaggio che esula dagli scopi di questo testo.
Il dialetto SQL da utilizzare nelle query è quello del database che si è scelto di utilizzare
per SAM.
1.9.1.1. Passaggio di parametri e interpolazione di stringhe
In questo paragrafo descriveremo il metodo usato per costruire la stringa di una query.
Poniamo caso di voler realizzare la query seguente:
SELECT * FROM songlist WHERE (ID = 123) AND (date_played < ‘2005-01-01’)

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;

function ExecSQL(SQL: String; Params: array di variabili): Integer;

Supponiamo ad esempio di volere resettare il bilanciamento di tutti i brani a zero; la query


che potremmo usare per capire quanti brani sono stati modificati è questa:
var conta : Integer = 0;
conta := ExecSQL(‘UPDATE songlist SET balance = 0’,[]);
WriteLn(IntToStr(cnt)+’ brani sono stati aggiornatì’);

Oppure cancellare tutti i brani di uno specifico artista:


var conta : Integer = 0;
conta := ExecSQL(‘DELETE FROM songlist WHERE artist LIKE :artist’,[‘Gigi D Alessio’]);
WriteLn(IntToStr(cnt)+’ brani sono stati cancellatì’);

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 funzione restituisce l’estensione da un nome file, comprendendo il punto.


EXTRACTFILEPATH

La stringa risultante contiene l’intero percorso del file dato come parametro tranne il nome
e l’estensione.
EXTRACTFILENAME

Estrae nome ed estensione dal parametro fornito.


1.9.2.2. Funzioni per il file system
Qui di seguito sono elencate alcune funzioni per il file system.
FUNZIONI BASE

Queste funzioni consentono di copiare, rinominare, cancellare o controllare se un file


esiste e non necessitano di particolari spiegazioni:
function CopyFile(SrcFileName, DestFile: String; FailIfExists: Boolean): Boolean;
function RenameFile(OldName: String; NewName: String): Boolean;
function DeleteFile(Filename: String): Boolean;
function FileExists(Filename: String): Boolean;
FUNZIONI DI ORGANIZZAZIONE FILE

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

WriteLn(‘Non è stato possibile scrivere sul file);

Lo script utilizza le funzioni FileToStr e StrToFile, che copiano rispettivamente un file su


una stringa e viceversa. Il resto del programma è composto da verifiche e stampe a video.
Il file viene salvato nuovamente inserendo la data corrente più dei caratteri di ritorno a
capo (#13 e #10).
Un altro modo per gestire i file di testo è quello di caricare il loro contenuto all’interno di
un oggetto TStringList, che permette di accedere ai contenuti di ciascuna linea del file di
testo.
Si consideri l’esempio successivo che riguarda l’importazione di un file M3U all’interno
di SAM. I File M3U sono dei file di testo contenenti un percorso di un file o di un flusso
per linea.
Un generico file M3U può essere così composto:
#EXTM3U
#EXTINF:366,Nome Artista 1–Nome Canzone 1
c:\musica\Nomeartista1-nomecanzone1.mp3
#EXTINF:258, Nome Artista 2 – Nome Canzone 2
c:\musica\NomeArtista2–NomeCanzone2.mp3
#EXTINF:285, Nome Artista 3 – Nome Canzone 3
c:\musica\NomeArtista3–NomeCanzone3.mp3
#EXTINF:-1,http://www.machebellawebradio.com:8000
http://www.machebellawebradio.com:8000

Lo script è diviso in 3 parti, la prima parte carica la playlist all’interno di un oggetto


TstringList. Per caricare il file all’interno dell’oggetto si utilizza la funzione
LoadFromFile:
const PlaylistFile = ‘c:\playlist.m3u’;
var List : TStringList;
var T : Integer;
List := TStringList.Create;
if FileExists(PlaylistFile) then
List.LoadFromFile(PlaylistFile)
else
WriteLn(‘La Playlist non esiste’);

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;

Lo script scorre le singole righe e le elimina se iniziano per “#”.


La terza parte dello script stampa a video la lista dei file o dei flussi della playlist e li
aggiunge alla coda:
for T := 0 to List.Count-1 do
begin
WriteLn(List[T]);
Queue.AddFile(List[T],ipBottom);
end;
List.Free;
FUNZIONI DI MANIPOLAZIONE FILE DI CONFIGURAZIONE

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

1.9.2.3. Scaricare file da http


PAL permette, tramite la funzione WebToFile, di scaricare da internet file musicali (ma
anche d’altro tipo) e gestirli. Il seguente script scarica un file da internet, lo salva in locale,
lo accoda e lo manda in onda:
const File_Remote = ‘http://indirizzosito/nomefile.mp3’;
const File_Local = ‘c:\content\nomefile.mp3’;
WebToFile(File_Local,File_Remote);
Queue.AddFile(File_Local,ipTop);
ActivePlayer.FadeToNext;
La funzione WebToFile è un mezzo molto potente. Funziona non soltanto per scaricare
file ma anche per eseguire operazioni come il kick di una precedente sorgente (come dal
caso visto precedentemente) o per eseguire un’azione attraverso un server web.
1.9.3. Maneggiare indirizzi remoti
SAM è in grado di suonare e trasmettere flussi audio provenienti da server di streaming,
per cui può benissimo funzionare da ritrasmettitore di web radio.
I flussi esterni possono essere statici (un MP3 su un sito internet) oppure dinamici (una
web radio).
Lo script seguente inserisce una web radio esterna all’interno della programmazione a un
certo orario e la rimuove a un altro orario. Il programma ha anche dei meccanismi di
controllo:
{ Configurazione delle variabili,
indirizzo della web radio ed orari di inizio e
termine della ritrasmissione.}
const ShowURL = ‘http://205.188.234.38:8002/’;
const StartTime = ‘01:01:00’;
const EndTime = ‘01:05:00’;
//implementazione.
var T : Integer;
PAL.Loop := True;
PAL.WaitForTime(StartTime);
Queue.Clear;
Queue.AddURL(ShowURL,ipTop);
ActivePlayer.FadeToNext;
T:=0;
while T < 20 do
begin
Queue.AddURL(ShowURL,ipBottom);
CAT[‘Tracks’].QueueBottom(smLRP,EnforceRules);
T:=T+1;
end;
PAL.WaitForTime(EndTime);
Queue.Clear;
ActivePlayer.FadeToNext;

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;

Il comando cruciale è chiaramente l’AddFile, ma è opportuno notare come, in base al


numero di iterazioni del processo, si blocchi e sblocchi l’esecuzione esclusiva.
1.9.4.3. Selezionare brani
Normalmente i brani, quando vengono selezionati per la rotazione, sono messi
direttamente in coda, ma cosa si può fare se si vuole fare un controllo ulteriore sul brano
inserito in coda prima di inserirlo definitivamente?
Questo script mostra una parziale soluzione:
var Song : TSongInfo;
Song := CAT[‘Tracks’].ChooseSong(smLRP,EnforceRules);
if Song <> nil then
begin
if Song[‘songtype’] = ‘S’ then
Queue.Add(Song,ipBottom);
end;

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.

Abs FormatDateTime Sin


AnsiCompareStr Frac Sinh
AnsiCompareText GetCurrentDir Sqr
AnsiLowerCase Hypot Sqrt
AnsiUpperCase Inc StrToDate
ChangeFileExt IncMonth StrToDateTime
CharAt InputBox StrToFloat
Chr Insert StrToFloatDef
CompareStr Int StrToInt
CompareText IntToHex StrToIntDef
Copy IsDelimiter StrToTime
Cos IsLeapYear Tan
Cosh LastDelimiter Tanh
Cotan Length Time
CreateDir Ln TimeToStr
Date LoadStringFromFile Trim
DateTimeToStr Log10 TrimLeft
DateToStr Log2 TrimRight
DayOfWeek LogN Trunc
DegToRad LowerCase UpperCase
Delete Max WriteGlobalVar
DeleteFile Min AppendStringToFile
EncodeDate Now ChDir
EncodeTime OkCancelDlg DecodeDate
Exp Ord DecodeTime
ExtractFileDir Pos Delete
ExtractFileDrive QuestionDlg ErrorDlg
ExtractFileExt QuotedStr InformationDlg
ExtractFileName RemoveDir Insert
ExtractFilePath RenameFile SaveStringToFile
FileExists Round SetCharAt
FileSearch SetCurrentDir ShowMessage
FloatToStr SetRandSeed

2.1.1. Metodo ABS


Il metodo Abs restituisce il valore assoluto dell’argomento X dato in ingresso.
X deve essere un intero o un valore numerico di tipo float.
Sintassi:
function Abs(X: Float): Float;

2.1.2. Metodo AnsiCompareStr


La funzione AnsiCompareStr confronta due stringhe date in ingresso e restituisce un
valore maggiore di zero se la prima stringa è maggiore (inteso come precedenza
alfabetica) della seconda, un valore minore di zero se la seconda è maggiore della prima,
zero se le due stringhe sono uguali, anche nel maiuscolo/minuscolo.
Sintassi:
function AnsiCompareStr(S1: String; S2: String): Integer;

2.1.3. Metodo AnsiCompareText


Il metodo AnsiCompareText lavora esattamente come AnsiCompareString (paragrafo
2.1.2) ma confronta le stringhe a prescindere da variazioni maiuscole o minuscole delle
lettere.
Sintassi:
function AnsiCompareText(S1: String; S2: String): Integer;

2.1.4. Metodo AnsiLowerCase


Il metodo AnsiLowerCase restituisce una stringa copia della precedente convertendo in
minuscolo tutti i caratteri.
Sintassi:
function AnsiLowerCase(Str: String): String;

2.1.5. Metodo AnsiUpperCase


Il metodo AnsiUpperCase è la funzione speculare di AnsiLowerCase (paragrafo 2.1.4).
Sintassi:
function AnsiUpperCase(Str: String): String;

2.1.6. Metodo ChangeFileExt


La funzione ChangeFileExt sostituisce l’estensione alla stringa data in ingresso con il
parametro FileName, con l’estensione fornita con il parametro Extension.
Ad esempio se la stringa fornita in ingresso è “C:/musica/canzone.mp3” e il valore di
Extension è “.ogg”, la stringa risultato della funzione ChangeFileExt sarà
“C:/musica/canzone.ogg”. Il file non viene rinominato, viene solo creata una stringa con
tale variazione.
Sintassi:
function ChangeFileExt(FileName: String; Extension: String): String;

2.1.7. Metodo CharAt


La funzione CharAt restituisce il carattere della stringa S presente in posizione x.
Attenzione, il primo carattere non è alla posizione 0 ma 1.
Sintassi:
function CharAt(S: String; x: Integer): String;

2.1.8. Metodo Chr


La funzione Chr restituisce l’equivalente ASCII dell’intero fornito in ingresso.
Sintassi:
function Chr(x: Integer): String;

2.1.9. Metodo CompareStr


A differenza della funzione AnsiCompareStr (paragrafo 2.1.2), la funzione CompareStr
confronta le due stringhe seguendo il valore ordinale a 8 bit dei caratteri e non
l’architettura locale del computer sul quale è eseguita la funzione. Questo può comportare
in linea teorica risultati diversi rispetto alla comparazione effettuata tramite
AnsiCompareStr.
Sintassi:
function CompareStr(S1: String; S2: String): Integer;

2.1.10. Metodo CompareText


A differenza della funzione AnsiCompareText (paragrafo 2.1.3), la funzione
CompareText confronta le due stringhe seguendo il valore ordinale a 8 bit dei caratteri e
non l’architettura locale del computer sul quale è eseguita la funzione. Questo può
comportare in linea teorica risultati diversi rispetto alla comparazione effettuata tramite
AnsiCompareText.
Sintassi:
function CompareText(S1: String; S2: String): Integer;

2.1.11. Metodo Copy


Il metodo Copy copia i caratteri di una stringa partendo dalla posizione passata come
secondo parametro, quindi copiando un numero di caratteri pari al numero indicato come
terzo parametro. Restituisce la stringa copiata.
Sintassi:
function Copy(S1: String; Index, Count : Integer): String;

2.1.12. Metodo CreateDir


La funzione CreateDir crea una nuova directory. Viene restituito true se la cartella è stata
creata, mentre viene restituito false se è stato riscontrato un qualsiasi errore (ad esempio:
cartella già esistente).
Sintassi:
function CreateDir(Dir: String): Boolean;
2.1.13. Metodo Date
La funzione Date ottiene la data attuale come tipo DateTime. La parte relativa all’orario
del file viene sempre impostata a mezzanotte.
Sintassi:
function Date: DateTime;

2.1.14. Metodo DateTimeToString


La funzione DateTimeToString converte una variabile DateTime fornita in ingresso in
una stringa.
Sintassi:
function DateTimeToStr(DT: DateTime): string;

2.1.15. Metodo DateToStr


La funzione DateToStr, a differenza della funzione DateTimeToString (paragrafo
2.1.14), converte in una stringa soltanto la data di un oggetto DateTime.
Sintassi:
function DateToStr(Date: DateTime): String;

2.1.16. Metodo DayOfWeek


La funzione DayOfWeek restituisce un valore compreso tra 1 e 7 in base al giorno della
settimana corrispondente alla variabile DateTime fornita in ingresso. Al numero 1
corrisponde domenica, mentre al 7 il sabato.
Sintassi:
function DayOfWeek(dt: DateTime): Integer;

2.1.17. Metodo Delete


La funzione Delete cancella un numero di caratteri di una stringa a partire da una certa
posizione. Il primo parametro indica la stringa, il secondo parametro indica il carattere dal
quale effettuare la cancellazione e il terzo parametro indica il numero di caratteri da
rimuovere.
Se il terzo parametro indica un numero più grande del numero di caratteri rimanenti da
index alla fine della stringa, essa verrà eliminata totalmente dalla posizione index in poi,
se invece il terzo parametro è minore di 0, non verranno cancellati caratteri.
Sintassi:
procedure Delete(var S: String; Index: Integer; Len: Integer);

2.1.18. Metodo DeleteFile


DeleteFile cancella fisicamente il file avente nome fornito come primo parametro. Se il
file non esiste o non può essere cancellato, viene restituito false, altrimenti true.
Sintassi:
function DeleteFile(Filename: String): Boolean;

2.1.19. Metodo EncodeDate


EncodeDate restituisce un valore del tipo DateTime, partendo dai valori passati come
parametro.
Il primo parametro è l’anno e deve essere compreso tra 1 e 9999.
Il secondo parametro è il mese, che va da 1 a 12.
Il terzo parametro è il giorno, che può andare da 1 a 31, in base al mese corrispondente.
In caso di data non possibile o non esistente, viene restituita un’eccezione chiamata
EconvertError.
Sintassi:
function EncodeDate(yy, mm, dd: Integer): DateTime;

2.1.20. Metodo EncodeTime


EncodeTime restituisce un valore DateTime sulla base dei parametri in ingresso.
Il primo parametro è l’ora, che può andare da 0 a 24.
Il secondo parametro è il minuto, che va da 0 a 59.
Il terzo parametro è il secondo che va da 0 a 59.
Il quarto parametro è il millisecondo, che va da 0 a 999.
In caso di orario non possibile o non esistente, viene restituita un’eccezione chiamata
EconvertError.
Il valore risultante è un numero compreso tra 0 e 1 (estremi inclusi) che indica la parte
frazionaria del giorno che rappresenta l’orario indicato.
Sintassi:
function EncodeTime(hh, mm, ss, ms: Integer): DateTime;

2.1.21. Metodo ExtractFileDir


La stringa risultante nel metodo ExtractFileDir è il nome di una directory che può essere
passata a funzioni come CreateDir, GetCurrentDir, RemoveDir e così via. La stringa è
vuota se il parametro FileName non contiene drive o directory.
Sintassi:
function ExtractFileDir(FileName: String): String;

2.1.22. Metodo ExtractFileDrive


La funzione ExtractFileDrive restituisce una stringa contenente il drive da un percorso
completo e assoluto di un file passato come parametro. Se il parametro non contiene un
percorso completo, la stringa risultante sarà vuota.
Sintassi:
function ExtractFileDrive(FileName: String): String;

2.1.23. Metodo ExtractFileExt


Il metodo ExtractFileExt restituisce l’estensione da un nome file completo, comprensivo
di punto delimitatore. La stringa è vuota se il file non ha estensione.
Sintassi:
function ExtractFileExt(FileName: String): String;

2.1.24. Metodo ExtractFileName


La stringa restituita da questa funzione contiene la parte finale del primo parametro, dal
quale viene cancellata la parte riguardante drive e cartelle, lasciando quindi soltanto nome
file ed estensione. La stringa restituita sarà uguale al primo parametro soltanto se il
parametro contiene solo un nome file.
Sintassi:
function ExtractFileName(FileName: String): String;

2.1.25. Metodo ExtractFilePath


La stringa restituita da questa funzione contiene la parte iniziale del primo parametro, dal
quale viene mantenuta soltanto la parte riguardante drive e cartelle. La stringa restituita
sarà uguale al primo parametro se non è presente un nome file nel percorso.
Sintassi:
function ExtractFilePath(FileName: String): String;

2.1.26. Metodo FileExists


Il metodo FileExists restituisce true se il file specificato come parametro esiste; se il file
non esiste, la funzione restituisce false.
Sintassi:
function FileExists(Filename: String): Boolean;

2.1.27. Metodo FileSearch


Il metodo FileSearch ricerca un file avente nome passato come primo parametro
all’interno di un gruppo di directory inserite all’interno del secondo parametro (separate
da punti e virgole su Windows e da virgole su sistemi Linux).
Se il file viene trovato, verrà restituito un percorso completo contenente il percorso del file
trovato, altrimenti restituirà una stringa vuota.
Si fa presente che non è scontato che l’ordine di ricerca sia anche quello presentato come
secondo parametro, per cui conviene sempre nominare i file in modo univoco per l’intero
database della radio.
Sintassi:
function FileSearch(Name: String; DirList: String): String;

2.1.28. Metodo FloatToStr


La funzione FloatToStr converte un valore float passato come primo parametro in una sua
rappresentazione stringa. La conversione usa un formato avente 15 cifre significative.
Sintassi:
function FloatToStr(v: Float): String;

2.1.29. Metodo FormatDateTime


FormatDateTime formatta il valore DateTime fornito dal secondo parametro secondo il
formato indicato dal primo.
Se il primo parametro è vuoto, viene utilizzato di default il formato C.
Sintassi:
function FormatDateTime(Format: String; dt: DateTime): String;

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

d Mostra il giorno come un numero senza lo zero iniziale

dd Mostra il giorno come un numero con lo zero iniziale

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

yy Mostra l’anno con due cifre

yyyy Mostra l’anno con quattro cifre

h Mostra l’ora senza lo zero iniziale


hh Mostra l’ora con lo zero iniziale

n Mostra i minuti senza lo zero iniziale

nn Mostra i minuti con lo zero iniziale

s Mostra i secondi senza lo zero iniziale

ss Mostra i secondi con lo zero iniziale

z Mostra i millisecondi senza lo zero iniziale

zzz Mostra i millisecondi con lo zero iniziale

t Mostra l’orario usando il formato previsto nella variabile globale ShortTimeFormat

tt Mostra l’orario usando il formato previsto nella variabile globale LongTimeFormat

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

Utilizza l’orologio di 12 ore e mostra il contenuto, se presente, della variabile globale


ampm
TimeAMString per gli orari mattutini e TimePMString per gli orari pomeridiani e serali

/ Mostra il separatore di data presente nella variabile globale DateSeparator

: Mostra il separatore di orario presente nella variabile globale TimeSeparator


2.1.30. Metodo GetCurrentDir
GetCurrentDir restituisce una stringa completa contenente il nome della directory
corrente.
Sintassi:
function GetCurrentDir: String;

2.1.31. Metodo IncMonth


IncMonth restituisce il valore del primo parametro chiamato Date, incrementato di un
valore pari al secondo parametro, chiamato NumberOfMonths. Il secondo parametro può
essere negativo, per tornare indietro nel tempo. Se l’operazione venisse fatta partendo da
un mese con 31 giorni, verso un mese con meno di 31 giorni, il risultato verrebbe allineato
all’ultimo giorno del mese.
La funzione restituisce un DateTime con l’incremento.
Sintassi:
function IncMonth(Date: DateTime; NumberOfMonths:
Integer): DateTime;

2.1.32. Metodo InputBox


Il metodo InputBox fa aprire una finestra che chiede all’utente un input testuale.
Il primo parametro, Caption, è il titolo della finestra.
Il secondo parametro, Prompt, è il testo mostrato all’interno della finestra.
Il terzo parametro, Def, è il testo standard che compare nella textbox.
Questa funziona blocca SAM fin quando non si inserisce un testo all’interno della textbox.
Attenzione al suo utilizzo.
Sintassi:
function InputBox(Caption: String; Prompt: String; Def: String): String;

♦ 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;

2.1.33. Metodo IsDelimiter


La funzione IsDelimiter controlla se la stringa contenuta all’interno del primo
parametro sia o non sia un delimitatore per la stringa indicata all’interno del secondo
parametro.
Il terzo parametro, index, è il numero del carattere dal quale verrà effettuato il controllo.
♦ Esempio
Supponiamo di avere la stringa “Ciao mamma guarda come mi diverto” e di
considerare la parola “mamma” come delimitatore. In questo caso una funzione
strutturata in questo modo:
IsDelimiter(“mamma”, “Ciao Mamma guarda come mi diverto”, “5”)

restituirebbe valore true.


2.1.34. Metodo IsLeapYear
La funzione IsLeapYear verifica se l’anno passato come parametro è bisestile.
Sintassi:
function IsLeapYear(Year: Integer): Boolean;

2.1.35. Metodo LastDelimiter


La funzione LastDelimiter mostra il numero di carattere dell’ultimo delimitatore
contenuto in una stringa. La lista dei delimitatori viene passata come primo parametro,
mentre come secondo parametro viene passata la stringa da verificare.
Sintassi:
function LastDelimiter(delims: String; s: String): Boolean;

♦ Esempio
Risultato := LastDelimiter(‘.:’,’c:\nomefile.est’);

Questo imposta Risultato a 12.


2.1.36. Metodo Length
La funzione Length restituisce il numero di caratteri di una stringa passata come
parametro.
Sintassi:
function Length(Str: String): Integer;

2.1.37. Metodo LoadStringFromFile


LoadStringFromFile carica i contenuti di un file in una stringa.
Il primo parametro contiene il percorso assoluto del file che deve essere convertito in
stringa.
Sintassi:
function LoadStringFromFile(Filename: String): String;
2.1.38. Metodo LowerCase
La funzione LowerCase restituisce la stringa passata come parametro, rendendo tutte le
lettere minuscole.
Sintassi:
function LowerCase(S: String): String;

2.1.39. Metodo Max


La funzione Max restituisce il più grande tra due numeri passati come parametri.
Sintassi:
function Max(v1: Float; v2: Float): Float;

2.1.40. Metodo Min


La funzione Min restituisce il più piccolo tra due numeri passati come parametri.
Sintassi:
function Min(v1: Float; v2: Float): Float;

2.1.41. Metodo Now


Now restituisce data e ora corrente.
Sebbene DateTime consenta di memorizzare millisecondi, la funzione Now arrotonda il
tempo al secondo più vicino.
Sintassi:
function Now: DateTime;

2.1.42. Metodo OkCancelDlg


OkCancelDlg mostra una finestra di dialogo con un tasto ok e un tasto cancel. Restituisce
true se viene premuto ok, false altrimenti.
Questa funzione blocca SAM fin quando non si preme ok o cancel. Attenzione al suo
utilizzo.
Sintassi:
function OkCancelDlg(Msg: String): Boolean;

2.1.43. Metodo Ord


Se X è una stringa contenente un tipo dati ordinabile, quale ad esempio integer o boolean,
il risultato della funzione Ord è il più piccolo intero standard che può contenere tutti i
valori del tipo dati passato come parametro.
Sintassi:
function Ord(X: String): Integer;

2.1.44. Metodo Pos


La funzione Pos è un metodo case-sensitive che cerca una sottostringa in una stringa più
grande. Restituisce l’indice del primo carattere della prima occorrenza della sottostringa.
Se non esiste un’occorrenza della sottostringa, allora verrà restituito zero.
Sintassi:
function Pos(SubStr: String; S: string): Integer;

2.1.45. Metodo QuestionDlg


Mostra una finestra di dialogo con un tasto YES e un tasto NO. Restituisce true se viene
premuto YES, false altrimenti.
Questa funzione blocca SAM fin quando non si preme yes o no. Attenzione al suo utilizzo.
Sintassi:
function QuestionDlg(Msg: String): Boolean;

2.1.46. Metodo QuotedStr


La funzione QuotedStr converte la stringa passata come parametro in una stringa tra
apici. Un apice è inserito all’inizio e alla fine della stringa e ogni apice è ripetuto.
Sintassi:
function QuotedStr(S: String): String;

2.1.47. Metodo RemoveDir


La funzione RemoveDir cancella la directory specificata dal primo parametro, ma solo se
vuota.
Viene restituito true se la cancellazione è avvenuta con successo, false se è invece
successo qualcosa che non doveva accadere.
Sintassi:
function RemoveDir(Dir: String): Boolean;

2.1.48. Metodo RenameFile


RenameFile rinomina un file cambiandogli il nome indicato dal primo parametro con
quello del secondo. Se l’operazione ha successo viene restituito true, se invece
l’operazione non si può fare (ad esempio perché non esiste un file con quel nome oppure
perché esiste già un file avente nome pari al secondo parametro) viene restituito false.
Sintassi:
function RenameFile(OldName: String; NewName: String): Boolean;

2.1.49. Metodo SetCurrentDir


La funzione SetCurrentDir imposta la directory corrente basandosi sulle indicazioni del
primo parametro. Se tale operazione può essere effettuata viene restituito true, altrimenti
false.
Sintassi:
function SetCurrentDir(Dir: String): Boolean;

2.1.50. Metodo StrToDate


La funzione StrToDate converte una stringa in un format data. Se questa operazione non
può essere fatta verrà lanciata l’eccezione EConvertError.
Sintassi:
function StrToDate(S: String): DateTime;

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

1998 0 1900 1903 1950 1968

2002 0 2000 2003 2050 2068

1998 50 1948 2003 1950 1968

2000 50 1950 2003 1950 1968

2002 50 1952 2003 2050 1968

2020 50 1970 2003 2050 2068

2020 10 2010 2103 2050 2068

2.1.51. Metodo StrToDateTime


Similarmente al caso della funzione StrToDate la funzione StrToDateTime converte una
stringa in un formato data. La stringa da convertire deve contenere oltre alla data anche un
orario.
Sintassi:
function StrToDateTime(S: String): DateTime;

2.1.52. Metodo StrToTime


Similarmente al caso della funzione StrToDate, la funzione StrToDateTime converte una
stringa in un formato data. La stringa da convertire deve contenere esclusivamente un
orario.
Sintassi:
function StrToTime(S: String): DateTime;

2.1.53. Metodo Time


La funzione Time restituisce il tempo corrente in formato DateTime.
Sintassi:
function Time: DateTime;

2.1.54. Metodo TimeToStr


TimeToStr è la funzione speculare rispetto a StrToTime. La conversione usa il formato
specificato dalla variabile globale LongTimeFormat.
Sintassi:
function TimeToStr(Time: DateTime): String;

2.1.55. Metodo Trim


Trim rimuove caratteri di controllo e spazi dall’inizio e dalla fine della stringa data in
ingresso.
Sintassi:
function Trim(S: String): String;

2.1.56. Metodo UpperCase


UpperCase opera specularmente rispetto alla funzione LowerCase (paragrafo 2.1.38).
La funzione Uppercase restituisce la stringa passata come parametro, rendendo tutte le
lettere maiuscole.
Sintassi:
function UpperCase(S: String): String;

2.1.57. Procedura AppendStringToFile


La procedura AppendStringToFile aggiunge una stringa alla fine dei contenuti di un file.
Se il file non esiste, ne verrà creato uno nuovo.
Sintassi:
procedure AppendStringToFile(Filename: String; Data: String);

2.1.58. Procedura ChDir


La procedura ChDir funziona in modo molto simile alla funzione SetCurrentDir
(paragrafo 2.1.49), cambiando la directory corrente con quella inserita all’interno del
primo parametro.
Sintassi:
procedure ChDir(NewDir: String);

2.1.59. Procedura DecodeDate


La procedura DecodeDate smonta il valore DateTime specificato nel primo parametro in
anno, mese e giorno. Se la data ha un valore anno negativo, allora tutti i parametri saranno
settati a 0.
Sintassi:
procedure DecodeDate(dt: DateTime; var yy, mm, dd: Integer);

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

2.1.60. Procedura DecodeTime


Similarmente alla funzione DecodeDate (paragrafo 2.1.59) la funzione DecodeTime
divide un valore DateTime in ora, minuti, secondi e millesimi.
Sintassi:
procedure DecodeTime(dt: DateTime; var hh, mm, ss, ms: Integer);

2.1.61. Procedura Delete


La procedura Delete rimuove una sottostringa di caratteri da una stringa partendo da un
particolare indice.
Il primo parametro è la stringa.
Il secondo parametro è l’indice del carattere dal quale iniziare la rimozione dei caratteri.
Il terzo parametro è la lunghezza della sottostringa da rimuovere.
Se il secondo parametro è maggiore della lunghezza della stringa, non viene rimosso alcun
carattere.
Se il terzo parametro indica più caratteri di quanti ne rimangano da rimuovere, viene
cancellato il resto della stringa da quel punto in poi.
Se il terzo parametro ha un valore minore di zero, non viene rimosso nulla.
Sintassi:
procedure Delete(var S: String; Index: Integer; Len: Integer);

2.1.62. Procedura ErrorDlg


La procedura ErrorDlg mostra un messaggio d’errore che vincola l’utente a cliccare sul
tasto ok.
Durante questo periodo SAM è bloccato, per cui porre massima attenzione all’utilizzo di
questa procedura.
Sintassi:
procedure ErrorDlg(Msg: String);

2.1.63. Procedura InformationDlg


La procedura InformationDlg mostra un messaggio che blocca SAM fin quando non
viene cliccato ok.
Sintassi:
procedure InformationDlg(Msg: String);

2.1.64. Procedura Insert


La funzione Insert inserisce la stringa passata come primo parametro nella stringa passata
come secondo, partendo dalla posizione indicata nel terzo.
Se Index, il terzo parametro, è settato a un valore inferiore a 1, verrà settato come 1, se
invece esso è maggiore della lunghezza della stringa, allora la funzione si trasformerà in
un Append, ovvero un inserimento in coda.
Se la stringa da inserire è vuota, non verrà fatta nessuna operazione.
La procedura lancerà l’eccezione EOutOfMemory se indisponibile ad allocare memoria
per la creazione della nuova stringa.
Sintassi:
procedure Insert(Source: String; var Dest: String; Index: Integer);

2.1.65. Procedura SaveStringToFile


La procedura SaveStringToFile salva una stringa passata come secondo parametro in un
file avente nome indicato nel primo parametro.
Sintassi:
procedure SaveStringToFile(Filename: String; Data: String);
2.1.66. Procedura SetCharAt
La procedura SetCharAt sostituisce il carattere presente alla posizione X (secondo
parametro) della stringa S (primo parametro) con la stringa C (terzo parametro).
Sintassi:
procedure SetCharAt(var S: String; x: Integer; c: String);

2.1.67. Procedura ShowMessage


ShowMessage mostra un messaggio a video e obbliga l’utente a premere ok per
continuare. La procedura blocca SAM, per cui attenzione al suo utilizzo.
Sintassi:
procedure ShowMessage(Msg: String);

2.2. Unità Encoders


L’unità Encoders contiene tutte le classi e le funzioni necessarie al controllo degli
encoder:
CLASSE TEncoder

METODI PROPRIETÀ

SongChange Format

Start Started

Stop Status

WriteScript StatusLong

CLASSE TEncoders

METODI PROPRIETÀ

SongChange Count

StartAll Encoders

StopAll Recording

WriteScript

2.2.1. Classe TEncoder


La classe TEncoder rappresenta l’istanza di un singolo encoder, strumento usato per
convertire dati audio di qualsiasi genere in un flusso audio omogeneo. Il flusso generato
può essere inviato tramite la rete internet a un server di streaming.
2.2.1.1. Metodo SongChange
Il metodo SongChange comunica all’encoder che un nuovo brano è stato avviato. Questa
funzione è avviata automaticamente da SAM Broadcaster ogni volta che un nuovo brano
viene avviato, ma in alcuni casi è utile richiamare questa funzione manualmente
all’interno di uno script PAL per aggiornare le informazioni del flusso in un determinato
momento.
Sintassi:
procedure SongChange(NewSong: TSongInfo);

♦ 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;

2.2.1.2. Metodo Start


Il metodo Start attiva l’encoder e inizia la codifica e il processo di invio del flusso audio.
Sintassi:
procedure Start;

2.2.1.3. Metodo Stop


Il metodo Stop è speculare rispetto al metodo Start ed effettua lo spegnimento
dell’encoder.
Sintassi:
procedure Stop;

2.2.1.4. Metodo WriteScript


Sebbene gli encoder supportino lo scripting sia per MP3 che per OGG, molti player non
gradiscono o, peggio, non supportano gli script all’interno dei metadata.
In caso di flussi Windows Media, è però possibile utilizzarli per molti scopi, quali ad
esempio l’apertura di finestre del browser o altri applicazioni.
Sintassi:
procedure WriteScript(Key: String; Data: String);

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

2.2.1.5. Proprietà Format


Format è una proprietà di sola lettura che restituisce il formato dell’encoder come tipo
stringa.
Sintassi:
property Format: String;

2.2.1.6. Proprietà Started


Started è una proprietà di sola lettura che restituisce vero se l’encoder è attivo, altrimenti
falso.
Sintassi:
property Started: Boolean;

2.2.1.7. Proprietà Status/StatusLong


Status e StatusLong sono proprietà di sola lettura che restituiscono lo stato corrente
dell’encoder come tipo stringa. Nel caso di StatusLong i dettagli sono maggiori.
Sintassi:
property Status: String;
property StatusLong: String;

2.2.2. Classe TEncoders


La classe TEncoders contiene la collezione di tutti gli encoder configurati all’interno
della regia in un particolare momento.
Per accedere alla classe e ai suoi metodi, si deve fare riferimento alla routine Encoders.
2.2.2.1. Metodo SongChange
Il metodo SongChange comunica all’encoder che un nuovo brano è stato avviato. Questa
funzione è avviata automaticamente da SAM Broadcaster ogni volta che un nuovo brano
viene avviato, ma in alcuni casi è utile richiamare questa funzione manualmente
all’interno di uno script PAL per aggiornare le informazioni del flusso in un determinato
momento.
Sintassi:
procedure SongChange(NewSong: TSongInfo);

La funzione è identica a quanto visto nel paragrafo 2.2.1.1


2.2.2.2. Metodo StartAll
Il metodo StartAll avvia tutti gli encoder contemporaneamente. La sintassi è la seguente:
procedure StartAll;

2.2.2.3. Metodo StopAll


Il metodo StopAll disabilita tutti gli encoder contemporaneamente.
Sintassi:
procedure StopAll;

2.2.2.4. Metodo WriteScript


Sebbene gli encoder supportino lo scripting sia per MP3 che per OGG, molti player non
gradiscono o, peggio, non supportano gli script all’interno dei metadata.
In caso di flussi Windows Media, è però possibile utilizzarli per molti scopi, quali ad
esempio l’apertura di finestre del browser o altri applicazioni.
Sintassi:
procedure WriteScript(Key: String; Data: String);

2.2.2.5. Proprietà Count


Count è una proprietà di sola lettura che restituisce il numero totale di encoder configurati
all’interno della regia come valore intero.
Lo script seguente utilizza la proprietà Count come contatore e attiva ogni encoder
configurato se non è già attivo in partenza.
Sintassi:
var I : Integer;
for I := 0 to Encoders.Count-1 do
if not Encoders[I].Started then
Encoders[I].Start;

2.2.2.6. Proprietà Encoders


Encoders è una proprietà di sola lettura che restituisce un singolo encoder all’indice
specificato. L’indice, come da corretta tradizione informatica, comincia da 0 e termina con
il numero di encoder totali meno uno (Encoders.Count – 1).
Sintassi:
property Encoders[Index: Integer]: TEncoder; default;

2.2.2.7. Proprietà Recording


Recording è una proprietà di lettura e scrittura di tipo booleano che, se settata come false
obbliga SAM a prendere l’audio dalla pipeline, mentre se settata come true, obbliga SAM
a prendere l’audio dall’ingresso della scheda audio. Ha la stessa funzionalità del tasto
rosso e blu della finestra encoder della regia.
Sintassi:
property Recording: Boolean;

2.3. Unità Relay


L’unità Relay contiene tutte le classi e le funzioni necessarie al controllo dei relay, che
vengono usati per estrarre le statistiche dai server di streaming in tempo reale.
CLASSE TRelay

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. Classe TRelay


La classe TRelay gestisce il singolo relay per l’estrazione delle statistiche. Ogni relay può
gestire un solo server di streaming.
2.3.1.1. Metodo Update
Normalmente SAM chiede a ciascun relay di aggiornare le proprie statistiche ogni 30,
massimo 60 secondi. Si può usare il metodo Update per forzare l’aggiornamento del relay.
Una volta che il metodo viene richiamato possono volerci alcuni secondi per
l’aggiornamento effettivo delle statistiche, per cui si consiglia di aspettare qualche
secondo all’interno dello script prima di utilizzare i dati aggiornati per qualsiasi motivo.
Sintassi:
procedure Update;

2.3.1.2. Proprietà Active


Active è una proprietà booleana di sola lettura che restituisce true se il server di streaming
è attivo e disponibile ad accettare ascoltatori oppure false nel caso contrario.
property Active: Boolean;

2.3.1.3. Proprietà Alias


Alias è una proprietà di sola lettura che restituisce il mountpoint del server di streaming
come tipo dato stringa.
Sintassi:
property Alias: String;

2.3.1.4. Proprietà Bitrate


Bitrate è una proprietà di sola lettura che restituisce il bitrate di codifica in kbps.
Sintassi:
property Bitrate: Integer;

2.3.1.5. Proprietà Details


Details è una proprietà di sola lettura che restituisce in formato stringa dei dettagli sul
relay, tipicamente l’indirizzo per l’accesso al flusso audio.
Sintassi:
property Details: String;

2.3.1.6. Proprietà Format


Format è una proprietà di sola lettura che restituisce in formato stringa il codec utilizzato
dall’encoder (MP3, OGG, WMA, ecc.).
Sintassi:
property Format: String;

2.3.1.7. Proprietà Host


Host è una proprietà di sola lettura che restituisce in formato stringa l’IP o l’alias DNS del
server di streaming.
Sintassi:
property Host: String;

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;

2.3.1.9. Proprietà IsPrivate


IsPrivate è una proprietà booleana di sola lettura che restituisce true se il server è privato
e non indicizzato da AudioRealm.com mentre restituisce false se il server è correttamente
indicizzato.
Sintassi:
property IsPrivate: Boolean;

2.3.1.10. Proprietà Port


Port è una proprietà di sola lettura che restituisce la porta di accesso al server di
streaming. Non è un dato disponibile per tutti i server di streaming.
Sintassi:
property Port: String;

2.3.1.11. Proprietà Server


Server è una proprietà di sola lettura che restituisce la tipologia di server di streaming
(Icecast, Shoutcast, ecc.).
Sintassi:
property Server: String;

2.3.1.12. Proprietà Status


Status è una proprietà di sola lettura che restituisce lo stato del server. Se tutto è
funzionante restituisce “OK”, altrimenti un messaggio d’errore.
Sintassi:
property Server: String;

2.3.1.13. Proprietà URL


URL è una proprietà di sola lettura che restituisce un URL speciale per l’ascolto del
flusso. Comando deprecato e poco utilizzato.
Sintassi:
property URL: String;

2.3.1.14. Proprietà Viewers


Viewers è una proprietà di sola lettura che restituisce il numero di ascoltatori connessi al
server in quel momento.
Sintassi:
property Viewers: Integer;

2.3.1.15. Proprietà Viewers_High


Viewers_High è una proprietà di sola lettura che restituisce il numero massimo di
ascoltatori che ha raggiunto il server da quando il relay è attivo.
Sintassi:
property Viewers_High: Integer;

2.3.1.16. Proprietà Viewers_Max


Viewers_Max è una proprietà di sola lettura che restituisce il numero massimo di
ascoltatori che il server può supportare in un determinato momento. Se il valore restituito
è – 1, il server non ha limiti nel numero di ascoltatori massimo, oppure il dato non è
disponibile.
Sintassi:
property Viewers_Max: Integer;

2.3.2. Classe TRelays


La classe TRelays contiene una collezione di tutti gli oggetti TRelay di SAM
Broadcaster. Occorre accedere a questa classe tramite la routine Relays.
2.3.2.1. Metodo AtLeastOneActive
AtLeastOneActive è un metodo booleano che restituisce true se almeno uno dei relay è
attivo e pronto a ricevere ascoltatori, altrimenti false.
Sintassi:
function AtLeastOneActive: Boolean;

2.3.2.2. Metodo Update


In modo simile a quanto visto per la funzione omonima presentata nel paragrafo 2.3.1.1, la
funzione Update aggiorna le statistiche di tutti i relay.
Sintassi:
procedure Update;

2.3.2.3. Proprietà Count


Count è una proprietà di sola lettura che determina il numero di relay configurati.
Sintassi:
property Count: Integer;

2.3.2.4. Proprietà Relays


La proprietà Relays consente l’accesso ai singoli oggetti Trelay. Il primo oggetto Trelay è
accessibile alla posizione 0, l’ultimo al risultato della funzione (TRelays.Count – 1).
Sintassi:
property Relays[Index: Integer]: TRelay; default;

2.3.2.5. Proprietà Viewers


Viewers è una proprietà di sola lettura che mostra il numero di ascoltatori in tutti i relay
attivi, compresi i privati.
Sintassi:
property Viewers: Integer

2.3.2.6. Proprietà Viewers_High


Viewers_High è una proprietà di sola lettura che mostra il numero massimo di ascoltatori
contemporanei registrati da tutti i server di streaming attivi sommati insieme.
Sintassi:
property Viewers_High: Integer;
2.3.2.7. Proprietà Viewers_Max
Viewers_Max è una proprietà di sola lettura che restituisce il numero massimo di
ascoltatori che la somma di tutti i server può supportare in un determinato momento. Se il
valore restituito è unlimited, c’è almeno un server che non ha limiti nel numero di
ascoltatori massimo, oppure il dato non è disponibile.
Sintassi:
property Viewers_Max: String;

2.4. Unità Player


L’unità Player contiene classi e oggetti per la gestione dei player della regia.
CLASSE TPlayer

METODI PROPRIETÀ ROUTINES

GetSongInfo CurTime DeckA

QueueSong Duration DeckB

Seek Status

Eject Volume

FadeToNext CanSeek

FadeToPlay Paused

FadeToPause Pitch

Next Tempo

Pause

Play

Stop

AGCLoadPresets

AGCSavePresets

2.4.1. Classe TPlayer


La classe TPlayer è utilizzata per avviare brani all’interno della regia. Usare le istanze di
questa classe per controllare i player.
2.4.1.1. Metodo GetSongInfo
La funzione GetSongInfo restituisce un oggetto del tipo TSongInfo contenente nil se non
è stato caricato un brano nel player, mentre se c’è un brano caricato estrarrà i dati
dell’oggetto TSongInfo.
Sintassi:
function GetSongInfo: TSongInfo;

2.4.1.2. Metodo QueueSong


QueueSong sostituisce il brano all’interno del player e carica un nuovo brano. Se
l’oggetto TSongInfo è stato creato manualmente, assicurati di avere inserito almeno il
nome del file, l’ID e la durata quali campi dell’oggetto.
Sintassi:
function QueueSong(Song: TSongInfo): Boolean;

♦ 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;

2.4.1.3. Metodo Seek


La funzione Seek sposta l’esecuzione del brano al tempo specificato nel parametro Time,
che deve essere specificato in millisecondi. L’intero restituito indica la posizione attuale
del brano in millisecondi.
Sintassi:
function Seek(Time: Integer): Integer;

2.4.1.4. Metodo Eject


Eject rimuove immediatamente il brano caricato sul player, anche se attualmente in
esecuzione.
Sintassi:
procedure Eject;

2.4.1.5. Metodo FadeToNext


FadeToNext comincia il fading del brano corrente e sostituisce il brano con il successivo
al termine del fading.
Sintassi:
procedure FadeToNext;

2.4.1.6. Metodo FadeToPlay


Se all’interno del player il brano è in pausa, la funzione FadeToPlay effettua un ingresso
graduale del brano dalla pausa al massimo volume.
Sintassi:
procedure FadeToPlay;

2.4.1.7. Metodo FadeToPause


Funzione complementare di FadeToPlay, effettua un fading dal massimo volume al
silenzio, quindi mette in pausa il brano.
Sintassi:
procedure FadeToPause;

2.4.1.8. Metodo Next


La funzione Next effettua il caricamento del brano successivo all’interno del player. Se il
player è attivo al momento dell’esecuzione del metodo Next, verrà comunque attivato il
nuovo brano.
Sintassi:
procedure Next;

2.4.1.9. Metodo Pause


La funzione Pause mette in pausa il brano attualmente attivo sul player. Se il brano è già
in pausa verrà riattivato.
Sintassi:
procedure Pause;

2.4.1.10. Metodo Play


La funzione Play attiva un brano precedentemente in pausa.
Sintassi:
procedure Play;

2.4.1.11. Metodo Stop


La funzione Stop ferma il brano attualmente in esecuzione.
Sintassi:
procedure Stop;

2.4.1.12. Metodo AGCLoadPreset


AGCLoadPreset carica un insieme di istruzioni per l’automatic gain control del player da
un file indicato come parametro stringa.
Sintassi:
procedure AGCLoadPreset(Filename : String);

2.4.1.13. Metodo AGCSavePreset


AGCSavePreset salva l’attuale insieme di istruzioni per l’automatic gain control del
player all’interno di un file impostato come parametro stringa.
Sintassi:
procedure AGCLoadPreset(Filename : String);

2.4.1.14. Proprietà CurTime


CurTime è una proprietà di sola lettura che restituisce l’attuale posizione del brano in
millisecondi.
Sintassi:
property CurTime: Integer;

2.4.1.15. Proprietà Duration


Duration è una proprietà di sola lettura che restituisce la durata complessiva del brano in
millisecondi.
Sintassi:
property Duration: Integer;

2.4.1.16. Proprietà Status


La proprietà Status restituisce lo stato del player, che è un valore tra queste costanti:
psPlaying = 0; il player sta eseguendo un brano
psReady = 1; player vuoto con nessun brano caricato
psQueued = 2; il player ha un brano caricato, ma in pausa.
Sintassi:
property Status: Integer;

2.4.1.17. Proprietà Volume


Volume è una proprietà in scrittura/lettura che legge o modifica il volume del player. Il
valore va da 0 (silenzio) a 255 (massimo volume).
Sintassi:
property Volume: Integer;

2.4.1.18. Proprietà CanSeek


CanSeek è una proprietà booleana di sola lettura che restituisce true se il player è in grado
di puntare a un particolare momento del brano, mentre restituisce false se non può farlo.
La proprietà ha come utilizzo principale quello di distinguere flussi audio in streaming da
brani con durata finita.
Sintassi:
property CanSeek: Boolean;

2.4.1.19. Proprietà Paused


Paused è una proprietà booleana in lettura/scrittura che legge o imposta lo stato di pausa
del player.
Sintassi:
property Paused: Boolean;

2.4.1.20. Proprietà Pitch


Pitch è una proprietà di lettura/scrittura che legge o imposta il pitch (frequenza dell’audio)
del player. Il range va da – 50, massima lentezza, a + 50, massima velocità. 0 rappresenta
la velocità nativa del brano.
Sintassi:
property Pitch: Integer;

2.4.1.21. Proprietà Tempo


Tempo è una proprietà di scrittura/lettura che cambia o legge la velocità dell’audio, senza
intervenire sulla frequenza. Il tempo, a differenza del pitch, non crea nell’audio
quell’effetto stile “Alvin Superstar”. La variazione del tempo è perfetta ad esempio per la
sincronizzazione dei battiti per un mixaggio. L’intervallo è lo stesso del pitch. 0
rappresenta il tempo nativo del brano.
Sintassi:
property Tempo: Integer;

2.4.1.22. Routine DeckA


La routine DeckA restituisce il deck A della regia.
Sintassi:
function DeckA: TPlayer;

2.4.1.23. Routine DeckB


La routine DeckB restituisce il deck B della regia.
Sintassi:
function DeckB: TPlayer;

2.4.1.24. Routine SoundFX


La routine SoundFX restituisce il player degli effetti della regia.
Sintassi:
function SoundFX: TPlayer;

2.4.1.25. Routine Aux1


La routine Aux1 restituisce il player ausiliare 1 della regia.
Sintassi:
function Aux1: TPlayer;

2.4.1.26. Routine Aux2


La routine Aux2 restituisce il player ausiliare 2 della regia.
Sintassi:
function Aux2: TPlayer;

2.4.1.27. Routine Aux3


La routine Aux3 restituisce il player ausiliare 3 della regia.
Sintassi:
function Aux3: TPlayer;

2.4.1.28. Routine ActivePlayer


La routine ActivePlayer restituisce il player attualmente attivo in regia secondo queste
regole:

PLAYER ATTIVO PLAYER RESTITUITO

Deck A Deck A

Deck B Deck B

Deck A e Deck B Deck A

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:

PLAYER ATTIVO PLAYER RESTITUITO

Deck A Deck A

Deck B Deck B

Deck A e Deck B Deck A

Nessuno NULL

Sintassi:
function IdlePlayer: TPlayer;

2.4.1.30. Routine QueuedPlayer


La routine QueuedPlayer restituisce il player con un brano caricato ma non attivo,
secondo queste regole:

PLAYER ATTIVO PLAYER RESTITUITO

Deck A Deck A

Deck B Deck B

Deck A e Deck B Deck A

Nessuno NULL

Sintassi:
function QueuedPlayer: TPlayer;

2.5. Unità PlaylistClasses


L’unità PlaylistClasses contiene classi e oggetti per la gestione della playlist e delle
categorie.
CLASSE TQueue

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;

2.5.1.2. Metodo NextInQueue


La funzione NextInQueue rimuove il primo brano in cima alla coda e lo restituisce in un
oggetto TSongInfo. Se la coda è vuota viene restituito nil.
Sintassi:
function NextInQueue: TSongInfo;

2.5.1.3. Metodo SongList


La funzione SongList restituisce un TDataSet con tutte le canzoni in coda. I campi
restituiti sono i seguenti:

CAMPO CONTENUTO

ID Estrae il record ID nella tabella queuelist

songID Estrae il record ID nella tabella songlist

sortID Attribuisce un ID per l’ordinamento dei dati

requestID Attribuisce un ID se il brano è stato richiesto

artist Estrae il nome dell’artista

title Estrae il titolo del brano

duration Estrae la durata del brano in millisecondi

Sintassi:
function SongList: TDataSet;

2.5.1.4. Metodo Add


La procedura Add aggiunge un brano alla coda. Il brano fornito in ingresso deve avere
almeno i campi ID (ID nella tabella songlist), artista, titolo e durata. Il secondo parametro
è la posizione che deve avere il brano in coda: 0 oppure IpTop se si vuole inserire in cima,
1 oppure IpBottom se si vuole inserire in fondo alla coda. Non è possibile indicare
posizioni intermedie.
Sintassi:
procedure Add(Song: TSongInfo; InsertPos: Integer);

2.5.1.5. Metodo AddDir


La procedura AddDir aggiunge un’intera directory alla coda. Il primo parametro accetta il
riferimento assoluto alla cartella, il secondo parametro è un booleano che deve essere
impostato come true se si vogliono includere le sottocartelle, oppure false se non si
vogliono includere. Il terzo parametro, esattamente come nella funzione Add, permette di
indicare dove inserire le canzoni all’interno della coda, se all’inizio (0 oppure IpTop) o
alla fine (1 oppure IpBottom).
Sintassi:
procedure AddDir(Dir: String; SubDir: Boolean; InsertPos: Integer);

2.5.1.6. Metodo AddFile


Aggiunge un file in coda alla posizione specifica. Il parametro Filename è il percorso
assoluto del file. Il parametro InsertPos, come nella funzione Add, indica la posizione in
cui verrà inserito il brano nella coda.
Sintassi:
procedure AddFile(Filename: String; InsertPos: Integer);

2.5.1.7. Metodo AddList


AddList aggiunge una playlist in coda alla posizione specifica. Il parametro Playlist è il
percorso assoluto della playlist. Le playlist supportate hanno estensioni PLS, M3U, ASX e
plain TXT. Il parametro InsertPos, come nella funzione Add, indica la posizione in cui
verrà inserita la playlist nella coda.
Sintassi:
procedure AddFile(Filename: String; InsertPos: Integer);

2.5.1.8. Metodo AddURL


AddURL aggiunge un flusso audio alla coda. Il parametro Insert Pos, come nella
funzione Add, indica la posizione in cui verrà inserito il flusso nella coda.
♦ Esempio
indirizzo.com:8000 SHOUTcast
Indirizzo.com/live Windows Media Stream
mic:// line-in (registra dalla scheda audio)
Sintassi:
procedure AddURL(URL: String; InsertPos: Integer);

2.5.1.9. Metodo Clear


Clear rimuove tutti i brani dalla coda.
Sintassi:
procedure Clear;

2.5.1.10. Metodo Delete


Delete cancella dalla coda il brano avente QueueID fornito come parametro.
Sintassi:
procedure Delete(QueueID: Integer);

2.5.1.11. Metodo Shuffle


Shuffle mescola i brani in coda in modo casuale.
Sintassi:
procedure Shuffle;

2.5.1.12. Proprietà Count


Count è una proprietà di sola lettura che restituisce il numero di brani presenti in coda.
Sintassi:
property Count: Integer;

2.5.1.13. Proprietà Duration


Duration è una proprietà di sola lettura che restituisce la durata totale della coda in
millisecondi.
Sintassi:
property Duration: Integer;

2.5.1.14. Proprietà ETA


ETA è una proprietà di sola lettura che restituisce un oggetto DateTime che specifica il
tempo esatto in cui l’ultimo brano in coda verrà trasmesso. Considera anche la durata
rimanente dei brani attualmente in trasmissione nei deck. ETA è l’acronimo per estimated
time of arrival.
Sintassi:
property ETA: DateTime;

2.5.2. Classe TCategory


La classe TQueue controlla le categorie della playlist.
2.5.2.1. Metodo ChooseSong
La funzione ChooseSong seleziona un brano dalla categoria usando una logica di
selezione data come parametro.
SelectMethod specifica quale logica usare, mentre se EnforceRules è impostato come
true, verrà scelto un brano che rispetti i vincoli di non ripetizione dei brani impostati nel
pannello di controllo o tramite altri script PAL. Restituisce un oggetto TSongInfo.
Sintassi:
function ChooseSong(SelectMethod: TSelectMethod; EnforceRules: Boolean): TSongInfo;
2.5.2.2. Metodo SongList
La funzione SongList restituisce l’elenco di tutti i brani disponibili nella categoria in
formato TDataSet.
Sintassi:
function SongList: TDataSet;

2.5.2.3. Metodo AddDir


La procedura AddDir aggiunge un’intera directory alla coda. Il primo parametro accetta il
riferimento assoluto alla cartella, il secondo parametro è un booleano che deve essere
impostato come true se si vogliono includere le sottocartelle, oppure false se non si
vogliono includere. Il terzo parametro, esattamente come nella funzione Add, permette di
indicare dove inserire le canzoni all’interno della coda, se all’inizio (0) o alla fine (1).
Sintassi:
procedure AddDir(Dir: String; SubDir: Boolean; InsertPos: Integer);

2.5.2.4. Metodo AddFile


AddFile aggiunge un file in categoria alla posizione specifica. Il parametro Filename è il
percorso assoluto del file. Il parametro InsertPos, come nella funzione Add, indica la
posizione in cui verrà inserito il brano nella coda.
Sintassi:
procedure AddFile(Filename: String; InsertPos: Integer);

2.5.2.5. Metodo AddFiles


La procedura AddFiles consente di aggiungere più file contemporaneamente alla
categoria. L’oggetto TStringList fornito come parametro contiene il percorso assoluto di
tutti i brani.
Sintassi:
procedure AddFiles(Files: TStringList; InsertPos: Integer);

2.5.2.6. Metodo AddList


AddList aggiunge una playlist alla categoria. Il parametro Playlist è il percorso assoluto
della playlist. Le playlist supportate hanno estensioni PLS, M3U, ASX e plain TXT. Il
parametro InsertPos, come nella funzione Add, indica la posizione in cui verrà inserita la
playlist nella coda.
Sintassi:
procedure AddFile(Filename: String; InsertPos: Integer);

2.5.2.7. Metodo AddURL


AddURL aggiunge un flusso audio alla categoria. Il parametro InsertPos, come nella
funzione AddDir, indica la posizione in cui verrà inserito il flusso nella categoria.
♦ Esempi
indirizzo.com:8000 SHOUTcast
indirizzo.com/live Windows Media Stream
mic:// Line-in (registra dalla scheda audio)
Sintassi:
procedure AddURL(URL: String; InsertPos: Integer);

2.5.2.8. Metodo Clear


Clear rimuove tutti i brani dalla categoria. Attenzione: non tutte le categorie supportano il
metodo Clear.
Sintassi:
procedure Clear;

2.5.2.9. Metodo Delete


Delete cancella dalla categoria il brano avente QueueID fornito come parametro. Se è
stato inserito più volte, ne verranno cancellate tutte le istanze.
Sintassi:
procedure Delete(QueueID: Integer);

2.5.2.10. Metodo QueueBottom


QueueBottom inserisce un brano alla fine della coda, selezionandolo in base alla logica
imposta dal parametro SelectMethod e dal flag EnforceRules.
Se il flag EnforceRules è attivato e nessun brano della categoria può essere selezionato
perché violerebbe le regole, allora non verrà aggiunto nessun brano in coda!
Sintassi:
procedure QueueBottom(SelectMethod: TSelectMethod; EnforceRules: Boolean);

♦ 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;

2.5.2.11. Metodo QueueTop


Queue Top inserisce un brano all’inizio della coda, selezionandolo in base alla logica
imposta dal parametro SelectMethod e dal flag EnforceRules.
Se il flag EnforceRules è attivato e nessun brano della categoria può essere selezionato
perché violerebbe le regole, allora non verrà aggiunto nessun brano in coda!
Sintassi:
procedure QueueTop(SelectMethod: TSelectMethod; EnforceRules: Boolean);

2.5.2.12. Metodo Shuffle


Shuffle mescola i brani in categoria in modo casuale. La procedura non funziona per
categorie autogenerate.
Sintassi:
procedure Shuffle;

2.5.2.13. Metodo Sort


Sort ordina la categoria in base al primo parametro. Se Ascending è impostato come true
l’ordine sarà ascendente, altrimenti discendente.
Sintassi:
procedure Sort(Field: String; Ascending: Boolean);

2.5.2.14. Proprietà Name


Name è una proprietà di sola lettura che restituisce il nome della categoria.
Sintassi:
property Name: String;
2.5.3. Classe TDirCategory
La classe TDirCategory consente di accedere a directory contenenti file multimediali
all’interno.
2.5.3.1. Metodo ChooseSong
La funzione ChooseSong seleziona un brano dalla directory usando una logica di
selezione data come parametro.
SelectMethod specifica quale logica usare, mentre, se EnforceRules è impostato come
true, verrà scelto un brano che rispetti i vincoli di non ripetizione dei brani impostati nel
pannello di controllo o tramite altri script PAL. Restituisce un oggetto TSongInfo.
Sintassi:
function ChooseSong(SelectMethod: TSelectMethod; EnforceRules: Boolean): TSongInfo;

2.5.3.2. Metodo QueueBottom


QueueBottom inserisce un brano alla fine della coda, selezionandolo in base alla logica
imposta dal parametro SelectMethod e dal flag EnforceRules.
Se il flag EnforceRules è attivato e nessun brano della categoria può essere selezionato
perché violerebbe le regole, allora non verrà aggiunto nessun brano in coda!
Il metodo non scansionerà la directory fisicamente. Terrà in considerazione solo i brani
attualmente presenti in database. Per scansionare la directory fisicamente occorre usare la
funzione Rescan.
Sintassi:
procedure QueueBottom(SelectMethod: TSelectMethod; EnforceRules: Boolean);

2.5.3.3. Metodo QueueTop


QueueTop inserisce un brano all’inizio della coda, selezionandolo in base alla logica
imposta dal parametro SelectMethod e dal flag EnforceRules.
Se il flag EnforceRules è attivato e nessun brano della categoria può essere selezionato
perché violerebbe le regole, allora non verrà aggiunto nessun brano in coda!
Il metodo non scansionerà la directory fisicamente. Terrà in considerazione solo i brani
attualmente presenti in database. Per scansionare la directory fisicamente occorre usare la
funzione Rescan.
Sintassi:
procedure QueueTop(SelectMethod: TSelectMethod; EnforceRules: Boolean);

2.5.3.4. Metodo Rescan


La funzione Rescan scansiona la directory ed effettua le seguenti operazioni:
1. aggiunge tutti i brani non elencati attualmente nella tabella songlist del database
2. rimuove tutti i brani che non esistono più fisicamente nella directory.
Effettuare scansioni su cartelle contenenti molti file può richiedere tempo e risorse, si
consiglia quindi di schedulare l’operazione correttamente.
Sintassi:
procedure Rescan;

♦ 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;

2.5.3.5. Proprietà Path


Path è una proprietà di sola lettura che restituisce l’indirizzo fisico della directory.
Sintassi:
property Path: String;

2.5.3.6. Proprietà SubDir


SubDir è una proprietà di sola lettura che verifica se vengono svolte operazioni anche
nelle sottodirectory della cartella indicate. Se la proprietà ha valore true, ci sono
operazioni effettuate sulle sottodirectory, altrimenti no.
Sintassi:
property SubDir: Boolean;

2.5.4. Classe TCategoryTree


La classe TCategoryTree consente di accedere a directory contenenti file multimediali
all’interno.
2.5.4.1. Proprietà Categories
Categories è una proprietà di sola lettura usata per accedere all’oggetto categoria su
SAM. Il parametro Name è il nome completo della categoria. La proprietà restituisce un
oggetto TCategory.
Sintassi:
property Categories[Name: String]: TCategory;

2.5.5. Classe TDirTree


La classe TDirTree consente di accedere all’oggetto TDirTree.
2.5.5.1. Proprietà DirCategories
Proprietà di sola lettura che accede alle cartelle. Il parametro Path contiene l’indirizzo
completo della cartella. La proprietà restituisce un oggetto TDirCategory.
Sintassi:
property DirCategories[Path: String]: TDirCategory;

2.5.6. Routine Cat


La routine Cat accede all’oggetto TCategoryTree che controlla tutte le categorie della
playlist.
Sintassi:
function Cat: TCategoryTree;

2.5.7. Routine Dir


La routine Dir accede all’oggetto TDirTree dove tutte le TDirCategory hanno la
proprietà SubDir settata come falsa, ovvero tutte le operazioni sulle directory non
includeranno sottodirectory.
Sintassi:
function Dir: TDirTree;

2.5.8. Routine Queue


La routine Queue permette di accedere all’oggetto TQueue.
Sintassi:
function Queue: TQueue;

2.5.9. Routine SubDir


La routine Dir accede all’oggetto TDirTree dove tutte le TDir
Category hanno la proprietà SubDir settata come true, ovvero tutte le operazioni sulle
directory includeranno sottodirectory.
Sintassi:
function SubDir: TDirTree;

2.5.10. Routine LoadPlaylist


LoadPlaylist copia tutti i brani di una playlist dentro una StringList. Il parametro
Filename deve contenere il percorso completo di un file PLS, M3U o ASX. Il parametro
Filelist deve contenere l’oggetto che memorizzerà i nomi dei file.
Sintassi:
procedure LoadPlayList(Filename: String; FileList: TStringList);

2.5.11. Routine ExtractRandom


La routine ExtractRandom estrae casualmente un brano da una playlist passata come
parametro.
Sintassi:
function ExtractRandom(Filename: String): String;

2.6. Unità PlaylistRules


L’unità PlaylistRules contiene classi e oggetti per il controllo delle regole di rotazione
della playlist.
CLASSE TPlaylistRules

PROPRIETÀ

MinAlbumTime

MinArtistTime

MinTitleTime

MinTrackTime

MinQueueSize

UseGhostQueue

2.6.1. Classe TPlaylistRules


La classe TPlaylistRules controlla le regole di rotazione della playlist.
2.6.1.1. Proprietà MinAlbumTime
MinAlbumTime è una proprietà di scrittura/lettura che imposta quanti minuti al minimo
debbano passare prima che la regia consideri la possibilità di inserire nuovamente un
brano di uno stesso album in rotazione.
Sintassi:
property MinAlbumTime: Integer;

2.6.1.2. Proprietà MinArtistTime


MinArtistTime è una proprietà di scrittura/lettura che imposta quanti minuti al minimo
debbano passare prima che la regia consideri la possibilità di inserire nuovamente un
brano di uno stesso artista in rotazione.
Sintassi:
property MinArtistTime: Integer;

2.6.1.3. Proprietà MinTitleTime


MinTitleTime è una proprietà di scrittura/lettura che imposta quanti minuti al minimo
debbano passare prima che la regia consideri la possibilità di inserire nuovamente un
brano con lo stesso titolo in rotazione.
Sintassi:
property MinTitleTime: Integer;

2.6.1.4. Proprietà MinTrackTime


MinTrackTime è una proprietà di scrittura/lettura che imposta quanti minuti al minimo
debbano passare prima che la regia consideri la possibilità di inserire nuovamente la stessa
traccia (si confronta il SongID) in rotazione.
Sintassi:
property MinTrackTime: Integer;

2.6.1.5. Proprietà MinQueueSize


MinQueueSize è una proprietà di scrittura/lettura che controlla o imposta il numero
minimo di brani che deve avere la coda in ogni momento. La regia controllerà la coda ogni
5 secondi e se la coda ha meno del numero di brani impostati in questa proprietà,
aggiungerà un brano alla coda.
Sintassi:
property MinQueueSize: Integer;

2.6.1.6. Proprietà UseGhostQueue


UseGhostQueue è una proprietà booleana di scrittura/lettura che si applica soltanto se la
proprietà MinQueueSize è 0.
Se la proprietà è impostata a true, SAM metterà in cache un brano che verrà riprodotto
successivamente senza sprecare troppa CPU con intense logiche di rotazione della playlist.
Sintassi:
property UseGhostQueue: Boolean;

2.7. Unità PlaylistControl


L’unità PlaylistControl contiene classi e oggetti per il controllo delle regole di rotazione
della playlist.
CLASSE TRequestPolicy

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;

2.7.1.2. Proprietà AlbumRule


AlbumRule è 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.1) per trasmettere un brano dello stesso album.
Sintassi:
property AlbumRule: Boolean;

2.7.1.3. Proprietà TitleRule


TitleRule è 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.3) per
trasmettere un brano con lo stesso titolo.
Sintassi:
property TitleRule: Boolean;

2.7.1.4. Proprietà TrackRule


TrackRule è 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.4) per trasmettere un brano avente lo stesso SongID.
Sintassi:
property TrackRule: Boolean;

2.7.1.5. Proprietà DailyLimit


DailyLimit è una proprietà di lettura/scrittura che imposta il numero massimo di richieste
che il visitatore può fare nelle 24 ore.
Sintassi:
property Dailylimit: Integer;

2.7.1.6. Proprietà DelayTime


DelayTime è una proprietà di lettura/scrittura che imposta il tempo in minuti che dovrà
attendere il visitatore per vedere la sua richiesta schedulata.
Sintassi:
property DelayTime: Integer;

2.7.1.7. Proprietà Enabled


Enabled è una proprietà booleana di lettura/scrittura che, se settata come true, permetterà
a SAM di gestire le richieste.
Sintassi:
property Enabled: Boolean;

2.7.1.8. Proprietà InsertPos


InserPos è una proprietà di scrittura/lettura che imposta la posizione all’interno della coda
dove andrà inserita la richiesta, secondo questo schema:
♦ ripNone = 0; non aggiunge la richiesta alla coda ma la mantiene nella lista richieste
♦ ripBottom = 1; aggiunge il brano in fondo alla coda (politica FIFO – first IN first OUT)
♦ ripTop = 2; aggiunge il brano all’inizio della coda (politica LIFO – last IN first OUT)
Sintassi:
property InsertPos: Integer;

2.7.1.9. Proprietà Limit


Limit è una proprietà di lettura/scrittura che indica il numero massimo di richieste che uno
stesso visitatore può fare durante l’intervallo indicato nella proprietà LimitInterval
(paragrafo 2.7.1.10)
Sintassi:
property Limit: Integer;

2.7.1.10. Proprietà LimitInterval


LimitInterval è una proprietà di lettura/scrittura che indica l’intervallo in minuti entro il
quale l’ascoltatore può effettuare un numero di richieste impostate nella proprietà Limit
(paragrafo 2.7.1.9)
Sintassi:
property LimitInterval: Integer;

2.7.2. Classe TRequestList


La classe TRequestList permette di gestire le richieste pendenti.
2.7.2.1. Metodo QueueBottom
Se ci sono richieste pendenti, la funzione QueueBottom posiziona la prima alla fine della
coda e restituisce l’oggetto TSongInfo che riporta all’interno le proprietà del brano. Se
non ci sono richieste pendenti, il richiamo di questo metodo restituirà nil. Questa funzione
forzerà le regole delle richieste, considerando le richieste con ritardo e avendo cura che le
richieste si adeguino alle regole di rotazione (chiamate comunemente “Do not repeat
rules”).
Sintassi:
function QueueBottom:TSongInfo;

2.7.2.2. Metodo QueueTop


Se ci sono richieste pendenti, la funzione QueueTop posiziona la prima all’inizio della
coda e restituisce l’oggetto TSongInfo che riporta all’interno le proprietà del brano. Se
non ci sono richieste pendenti, il richiamo di questo metodo restituirà nil. Questa funzione
forzerà le regole delle richieste, considerando le richieste con ritardo e avendo cura che le
richieste si adeguino alle regole di rotazione (chiamate comunemente “Do not repeat
rules”).
Sintassi:
function QueueTop:TSongInfo;

2.7.2.3. Metodo GetNextRequest


La proprietà restituisce l’oggetto contenente la successiva richiesta pendente. Se non ce ne
sono, verrà restituito nil. La proprietà non rimuoverà il brano dalla lista richieste.
Sintessi:
function GetNextRequest:TSongInfo;

2.8. Unità SongInfo


L’unità SongInfo contiene classi e oggetti per il controllo delle regole di rotazione della
playlist.
CLASSE TSongInfo

COSTRUTTORE PROPRIETÀ

Create Values

2.8.1. Classe TSongInfo


La classe TSongInfo è usata per conservare informazioni sui brani.
2.8.1.1. Costruttore Create
Il costruttore crea una nuova istanza dell’oggetto TSongInfo.
Sintassi:
constructor Create;

♦ 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;

2.8.1.2. Proprietà Values


La proprietà di lettura e scrittura Values permette di leggere o impostare le informazioni
dell’oggetto TSongInfo.
Sintassi:
property Values[Name: String]: Variant; default;

2.9. Unità SQL


L’unità SQL permette la comunicazione diretta con il database di SAM Broadcaster.
CLASSE TDataSet

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;

2.9.1.2. Metodo EOF


EOF è una funzione booleana che restituisce true se il dataset è sull’ultimo record del
database.
Sintassi:
function EOF: Boolean;

2.9.1.3. Metodo Append


Append aggiunge un nuovo record alla fine del dataset.
Sintassi:
procedure Append;

2.9.1.4. Metodo Cancel


Cancel elimina ogni modifica effettuata al record.
Sintassi:
procedure Cancel;

2.9.1.5. Metodo Edit


Edit imposta il dataset in modalità scrittura, permettendo la modifica dei valori del record.
Sintassi:
procedure Edit;

2.9.1.6. Metodo First


First muove il puntatore al dataset al primo record.
Sintassi:
procedure First;

2.9.1.7. Metodo Insert


Insert inserisce un nuovo record nel dataset.
Sintassi:
procedure Insert;

2.9.1.8. Metodo Delete


Delete cancella il record corrente dal dataset.
Sintassi:
procedure Delete;
2.9.1.9. Metodo Last
Last muove il puntatore all’ultimo record del dataset.
Sintassi:
procedure Last;

2.9.1.10. Metodo Next


Next muove il puntatore al successivo record del dataset.
Sintassi:
procedure Next;

2.9.1.11. Metodo Post


Post salva le modifiche effettuate al record nel database.
Sintassi:
procedure Post;

2.9.1.12. Metodo Previous


Previous muove il puntatore al precedente record del dataset.
Sintassi:
procedure Previous;

2.9.1.13. Proprietà Values


La proprietà di lettura/scrittura Values è utilizzata per leggere o impostare i valori dei
campi di un record.
Per potere effettuare queste modifiche, occorre prima abilitare il dataset alla scrittura con
la funzione Edit (paragrafo 2.9.1.5).
Sintassi:
property Values[Fieldname: String]: Variant; default;

2.9.2. Routine ExecSQL


ExecSQL esegue una query SQL che non ritorna nessun risultato (come ad esempio i
comandi Update o Delete).
Il parametro SQL è la query SQL, Params rappresenta i parametri opzionali all’interno
della query SQL.
L’intero restituito rappresenta il numero di righe coinvolte.
Sintassi:
function ExecSQL(SQL: String; Params: Array of Variant): Integer;

2.9.3. Routine Query


Esegue una query SQL che restituisce un risultato.
Il parametro SQL è la query SQL, Params rappresenta i parametri opzionali all’interno
della query SQL.
Se il parametro ReadOnly è impostato a false allora sarà possibile modificare i record del
dataset.
La routine restituisce un oggetto TDataSet che permette l’accesso ai risultati.
Sintassi:
function Query(Sql: String; Params: Array of Variant; ReadOnly: Boolean): TDataSet;

L’esempio mostra l’elenco dei brani trasmessi negli ultimi 20 minuti:


var D : DateTime;
var Q : TDataSet;
D := T[‘-00:20:00’];
Q := Query(‘SELECT * FROM songlist WHERE date_played >= :time ORDER BY date_played DESC’,[D],False);
Q.First;
while not Q.EOF do
begin
WriteLn(Q[‘filename’]);
Q.Next;
end;
Q.Free;

2.10. Unità PALClasses


L’unità PALClasses contiene classi e oggetti per il controllo delle regole di rotazione della
playlist.
COMPONENTE TStringList

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

COSTRUTTORE METODI PROPRIETÀ

Create Add Items

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

2.10.1. Componente TStringList


TStringList mantiene una lista di stringhe.
Usa un oggetto StringList per memorizzare e manipolare una lista di stringhe.
TStringList implementa le proprietà astratte e i metodi introdotti da TStrings, ampliando
con nuove proprietà ed eventi per:
♦ ordinare le stringhe in lista
♦ proibire la duplicazione delle stringhe in liste ordinate
♦ rispondere a cambiamenti nei contenuti della lista
♦ controllare dove siano localizzate le stringhe, come siano ordinate e identificare i
duplicati in modalità sensibile o insensibile alle maiuscole.
2.10.1.1. Metodo Find
Find è una funzione booleana per la verifica di presenza di stringhe in una lista. Va usato
su liste ordinate dove la stringa S va aggiunta. Se la stringa S o una stringa che differisce
da S solo per il maiuscolo/minuscolo è presente, allora la funzione restituirà true,
altrimenti false. L’indice dove S dovrebbe andare è indicato nel parametro Index. Il valore
di Index parte da 0, per cui la prima stringa ha valore 0, la successiva 1 e così via.
Per le liste disordinate, usare il metodo IndexOf piuttosto che Find.
Sintassi:
function Find(S: String; var Index: Integer): Boolean;

2.10.1.2. Metodo Sort


La funzione Sort ordina le stringhe in lista in modo ascendente.
Eseguire questa funzione per ordinare le stringhe in una lista che abbia la proprietà Sorted
settata come false. Le liste che hanno la proprietà Sorted impostata a true sono
automaticamente ordinate. Il metodo Sort usa la funzione AnsiCompareStr per ordinare
le stringhe con proprietà CaseSensitive impostata come true e il metodo
AnsiCompareText quando CaseSensitive è false. Per utilizzare una comparazione
personalizzata, utilizzare il metodo CustomSort.
Sintassi:
procedure Sort;

2.10.1.3. Proprietà Duplicates


Duplicates è una proprietà di lettura/scrittura che specifica se le stringhe duplicate
possano o meno essere aggiunte alla lista ordinata. Impostare questa proprietà per istruire
SAM su cosa debba succedere se viene fatto un tentativo di aggiungere una stringa
duplicata a una lista ordinata. La proprietà CaseSensitive controlla se due stringhe siano
da considerarsi duplicate se differiscono solo nel maiuscolo/minuscolo.
I valori di Duplicates appartengono all’enumerazione TDuplicates, che accetta i seguenti
valori:

VALORE SIGNIFICATO

dupIgnore Ignora il tentativo di aggiunta della stringa duplicata

Restituisce un’eccezione EStringListError quando viene fatto il tentativo di inserimento della stringa
dupError
duplicata

dupAccept Permette la duplicazione

La proprietà Duplicates va inserita chiaramente prima di aggiungere la stringa in lista.


Settare la proprietà Duplicates a dupIgnore o dupError non influisce sulla presenza di
stringhe duplicate già presenti in lista.
La proprietà non interviene se la lista non è ordinata.
Sintassi:
property Duplicates: TDuplicates;

2.10.1.4. Proprietà Sorted


Proprietà di lettura/scrittura che specifica se le stringhe in lista debbano essere
automaticamente ordinate.
Impostare questa proprietà come true comporta che le stringhe in lista vengano
automaticamente ordinate in modo ascendente. Se il metodo è impostato a false, la stringa
può comunque essere ordinata in modo ascendente in qualsiasi momento richiamando il
metodo Sort (paragrafo 2.10.1.2).
Quando Sorted è impostato al valore true, non usare il metodo Insert ma piuttosto il
metodo Add, che inserirà la stringa nella sua posizione corretta. Se invece Sorted è
impostato al valore false, usare Insert per inserire la stringa in una posizione arbitraria,
oppure Add per inserirla in coda.
Sintassi:
property Sorted: Boolean;

2.10.2. Componente TIntegerHashTable


Il componente TIntegerHashTable contiene una lista hash indicizzata tramite numeri
interi.
2.10.2.1. Costruttore Create
Create crea un nuovo oggetto TIntegerHashTable.
Sintassi:
constructor Create;

2.10.2.2. Metodo Get


Get restituisce l’oggetto all’indice specificato al parametro Key.
Sintassi:
function Get(Key: Integer): TObject;

2.10.2.3. Metodo HasKey


La funzione booleana HasKey restituisce true se la chiave è presente in lista, false
altrimenti.
Sintassi:
function HasKey(Key: Integer): Boolean;

2.10.2.4. Metodo RemoveKey


RemoveKey rimuove dalla lista l’elemento con chiave specificata nel parametro Key.
Sintassi:
function RemoveKey(Key: Integer): TObject;

2.10.2.5. Metodo Put


Put aggiunge un nuovo elemento alla lista e gli associa una chiave.
Sintassi:
procedure Put(Key: Integer; Value: TObject);

2.10.3. Componente TStringHashTable


Il componente TStringHashTable contiene una lista hash indicizzata tramite stringhe.
2.10.3.1. Costruttore Create
Create crea un nuovo oggetto TStringHashTable.
Sintassi:
constructor Create;

2.10.3.2. Metodo Get


Get restituisce l’oggetto all’indice specificato al parametro Key.
Sintassi:
function Get(Key: String): TObject;
2.10.3.3. Metodo HasKey
La funzione booleana HasKey restituisce true se la chiave è presente in lista, false
altrimenti.
Sintassi:
function HasKey(Key: String): Boolean;

2.10.3.4. Metodo RemoveKey


RemoveKey rimuove dalla lista l’elemento con chiave specificata nel parametro Key.
Sintassi:
function RemoveKey(Key: String): TObject;

2.10.3.5. Metodo Put


Put aggiunge un nuovo elemento alla lista e gli associa una chiave.
Sintassi:
procedure Put(Key: String; Value: TObject);

2.10.4. Classe TList


TList mantiene una lista di TObjects generici.
2.10.4.1. Costruttore Create
Create crea un nuovo oggetto TList.
Sintassi:
constructor Create;

2.10.4.2. Metodo Add


Add aggiunge un elemento alla lista dal parametro Obj, restituisce l’indice dell’oggetto
appena aggiunto.
Sintassi:
function Add(Obj: TObject): Integer;

2.10.4.3. Metodo Count


La funzione Count restituisce il numero di elementi presenti nella lista.
Sintassi:
function Count: Integer;

2.10.4.4. Metodo IndexOf


La funzione IndexOf restituisce l’indice di un puntatore all’array di elementi. Specificare
il puntatore come parametro Item. Il primo elemento dell’array ha indice 0, il secondo ha
indice 1 e così via. IndexOf restituisce l’indice della prima occorrenza se l’elemento
puntato è presente più volte.
Sintassi:
function IndexOf(Item: TObject) : Integer;

2.10.4.5. Metodo Remove


La funzione Remove cancella dalla lista la prima occorrenza dell’elemento indicato nel
parametro Item. La funzione è utile qualora non si conosca l’indice dell’elemento. Il
valore restituito dalla funzione è l’indice dell’elemento prima che esso venga rimosso
dalla lista. Dopo che un elemento viene rimosso, tutti gli elementi che lo seguono avranno
l’indice diminuito di uno rispetto al valore precedente e anche il valore di Count si riduce
di uno. Se l’array ha più di una copia dell’oggetto indicato come parametro ne viene
rimosso soltanto uno.
Sintassi:
function Remove(Item: TObject): Integer;

2.10.4.6. Metodo Clear


La funzione Clear cancella l’array di elementi e imposta il Count a 0. Il metodo Clear
cancella la memoria usata per memorizzare l’array di elementi e imposta la capacità a 0.
Sintassi:
procedure Clear;

2.10.4.7. Metodo Delete


La funzione Delete rimuove l’elemento specificato alla posizione indicata dal parametro
Index. L’indice parte da 0, per cui il primo elemento vale 0, il secondo 1 e così via.
Richiamare la funzione Delete muove in avanti tutti gli elementi che seguono l’elemento
rimosso, e riduce il valore di Count.
Sintassi:
procedure Delete(Index: Integer);

2.10.4.8. Metodo Insert


Insert aggiunge un oggetto all’array di elementi alla posizione specificata dal parametro
Index.
Sintassi:
procedure Insert(Index: Integer; Obj: TObject);

La funzione Insert aggiunge elementi al centro dell’array. Il parametro Index parte da 0,


così la prima posizione dell’array ha indice 0, il secondo 1 e così via. Insert aggiunge
l’elemento alla posizione indicata dal parametro, spostando gli elementi seguenti in avanti
di una posizione. La funzione incrementa il valore di Count e se necessario alloca
memoria aumentando il valore di Capacity.
2.10.4.9. Proprietà Items
Proprietà di lettura/scrittura che accede all’elemento memorizzato in lista. Il primo
elemento è posizionato all’indice 0.
Sintassi:
property Items[x: Integer]: TObject;

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;

2.10.5. Classe TStrings


La classe TStrings è una classe base e astratta. Utilizza piuttosto la classe TStringList
quando vuoi creare un oggetto.
2.10.5.1. Metodo Add
La funzione Add aggiunge una stringa alla fine della lista e restituisce l’indice della nuova
stringa.
Sintassi:
function Add(Str: String): Integer;

2.10.5.2. Metodo AddObject


AddObject aggiunge alla lista una stringa e il suo oggetto associate. La funzione
restituisce l’indice della nuova stringa e oggetto correlato. L’oggetto TStrings non
incorpora l’oggetto aggiunto in questo modo, che continua a esistere anche se l’istanza
della classe TStrings continua a esistere. L’oggetto deve essere esplicitamente distrutto
dall’applicazione.
Sintassi:
function AddObject(S: String; AObject: TObject): Integer;
2.10.5.3. Metodo IndexOf
IndexOf restituisce la posizione della stringa nella lista. La funzione ottiene la posizione
della prima occorrenza della stringa S o di una stringa che differisce da S solo per il
maiuscolo/minuscolo. L’indice delle posizioni parte da 0. Se la stringa non è presente,
viene restituito – 1.
Sintassi:
function IndexOf(S: String): Integer;

2.10.5.4. Metodo IndexOfName


IndexOfName restituisce la posizione della prima stringa con valore corrispondente al
parametro Name fornito, ammesso che sia nella forma Name = VALORE.
La funzione ottiene la posizione della prima occorrenza della stringa Name o di una
stringa che differisce da Name solo per il maiuscolo/minuscolo. L’indice delle posizioni
parte da 0. Se la stringa non è presente, viene restituito – 1.
Sintassi:
function IndexOfName(Name: String): Integer;

2.10.5.5. Metodo IndexOfObject


IndexOfObject restituisce la posizione della prima stringa avente l’oggetto AObject
come oggetto associato.
La funzione ottiene la posizione della prima occorrenza della stringa contenente l’oggetto
associato, che va indicato come parametro. L’indice delle posizioni parte da 0. Se la
stringa non è presente, viene restituito il valore – 1.
Sintassi:
function IndexOfObject(AObject: TObject): Integer;

2.10.5.6. Metodo AddStrings


Il metodo AddStrings consente di aggiungere alla lista le stringhe di un altro oggetto
TStrings. Se entrambi gli oggetti TStrings supportano l’associazione di oggetti alle loro
stringhe, i riferimenti agli oggetti associati verranno contestualmente copiati.
Sintassi:
procedure AddStrings(Strings: TstringList);

2.10.5.7. Metodo Clear


Il metodo Clear, applicato principalmente alle classi derivate da TStrings, cancella tutte
le stringhe della lista e rimuove i riferimenti agli oggetti associati.
Sintassi:
procedure Clear;
2.10.5.8. Metodo Delete
Il metodo Delete è una funzione astratta per cancellare una specifica stringa dalla lista.
Le classi derivate da TStrings implementano un metodo Delete per rimuovere una
specifica stringa dalla lista. Se un oggetto è associato con la stringa, il suo riferimento
viene rimosso contestualmente. Il parametro Index fornisce la posizione della stringa,
considerando che 0 rappresenta la prima stringa, 1 la seconda e così via.
Sintassi:
procedure Delete(Index: Integer);

2.10.5.9. Metodo Exchange


Exchange scambia le posizioni di due stringhe (e di eventuali oggetti associati) fornite
come parametro.
Sintassi:
procedure Exchange(Index1: Integer; Index2: Integer);

2.10.5.10. Metodo Insert


Insert è un metodo astratto per l’inserimento di stringhe in posizione specificata.
Le classi che derivano da TStrings implementano un metodo per aggiungere la stringa S
alla lista nella posizione specificata dal parametro Index. Se la stringa ha oggetti associati
si deve usare il metodo InsertObject (paragrafo 2.10.5.11).
Sintassi:
procedure Insert(Index: Integer; S: String);

2.10.5.11. Metodo InsertObject


InsertObject inserisce una stringa all’interno della posizione definite dal parametro Index
e la associa all’oggetto indicato con il parametro AObject.
Sintassi:
procedure InsertObject(Index: Integer; S: String; AObject: TObject);

2.10.5.12. Metodo LoadFromFile


LoadFromFile riempie la lista con stringhe prese dalle righe di un file di testo fornito
come parametro. Viene richiamata ciclicamente la funzione Add (paragrafo 2.10.5.1) fino
al termine del file di testo fornito.
Sintassi:
procedure LoadFromFile(Filename: String);

2.10.5.13. Metodo Move


Move cambia la posizione di una stringa in lista. Il parametro CurIndex rappresenta la
posizione iniziale, mentre NewIndex la posizione finale. Se la posizione indicata in
NewIndex è occupata, verranno spostate in avanti tutte le stringhe seguenti e quella alla
posizione corrispondente a NewIndex.
Sintassi:
procedure Move(CurIndex: Integer; NewIndex: Integer);

2.10.5.14. Metodo SaveToFile


SaveToFile esporta le stringhe della lista in un file indicato come parametro. Ogni stringa
sarà indicata in una riga separata.
Sintassi:
procedure SaveToFile(Filename: String);

2.10.5.15. Metodo CommaText


CommaText è una proprietà di lettura/scrittura che elenca le stringhe di un oggetto
TStrings in formato SDF.
Sintassi:
property CommaText: String;

Tutte le stringhe verranno espresse in un’unica stringa, separate da virgole. Tutte le


stringhe che contengono spazi, virgole o apostrofi saranno comprese in doppi apici.
Ad esempio, se la lista contiene queste stringhe:
Stri,nga 1
Stri”nga 2
Stringa 3
Stringa4

Allora la funzione CommaText restituirà:


“Stri,nga 1”,”Stri””nga 2”,”Stringa 3”,Stringa4

Le stringhe sono separate da virgole o da spazi e occasionalmente racchiuse tra doppi


apici. I doppi apici che fanno parte della stringa sono ripetuti per distinguerli dagli apici
che circondano la stringa. Tutti gli spazi e le virgole che non sono contenuti dentro doppi
apici vengono considerati delimitatori. Due virgole l’una accanto all’altra rappresentano
una stringa vuota, ma due spazi vicini vengono ignorati. Ad esempio se il risultato di una
funzione CommaText fosse questo:
“Stri,nga 1”, “Stri””nga 2” , Stringa 3,Stringa4

allora la lista conterrebbe:


Stri,nga 1
Stri”nga 2
Stringa
3
Stringa4

2.10.5.16. Proprietà Count


Count è una proprietà astratta (da implementare nelle classi che derivano da TStrings)
che restituisce il numero di stringhe della lista.
Sintassi:
property Count: Integer;

2.10.5.17. Proprietà Names


Names è una proprietà di lettura/scrittura che restituisce la parte “nome” di una stringa
strutturata in forma NOME = VALORE tipica ad esempio dei file con estensione .INI. Se
la stringa non è nella forma indicata verrà restituita una stringa nulla. Si veda come
esempio di funzionamento il metodo IndexOfName (paragrafo 2.10.5.4).
Sintassi:
property Names[Index: Integer]: String;

2.10.5.18. Proprietà Objects


La proprietà Objects non ha funzione nella classe TStrings, deve essere implementata
nelle classi che derivano da essa, dove il funzionamento del metodo è quello di restituire
l’oggetto associato alla stringa avente posizione indicata nel parametro Index.
Sintassi:
property Objects[Index: Integer]: TObject;

2.10.5.19. Proprietà Strings


La proprietà Strings è standard per la classe TStrings e tutte le classi che da essa derivano
devono implementare una funzione d’accesso che restituisca la stringa dato il parametro
indice in ingresso. Essendo standard, è possibile accettare entrambe le forme proposte:
NomeStringa.Strings[0] := ‘Questa è la prima stringa’;
NomeStringa[0] := ‘Questa è la prima stringa’;

Sintassi:
property Strings[Index: Integer]: String; default;

2.10.5.20. Proprietà Text


La proprietà Text esporta le stringhe di una lista in un’unica stringa che separa le varie
stringhe esportate con un ritorno carrello. Se all’interno della lista di stringhe è presente
qualche stringa contenente dei ritorni carrello (i famosi “a capo”) allora l’elenco delle
stringhe potrà risultare maggiore rispetto al valore di Count. Per evitare ambiguità, se non
si è sicuri che le stringhe siano prive di ritorni carrello o tabulazioni, si consiglia di
utilizzare la funzione CommaText (paragrafo 2.10.5.15) o altre similari definite dal
linguaggio.
Sintassi:
property Text: String;

2.10.5.21. Proprietà Values


Values è una proprietà speculare rispetto a Names (paragrafo 2.10.5.17) che restituisce i
valori delle stringhe formate in modalità NOME = VALORE, tipiche ad esempio dei file
“.ini”.
Sintassi:
property Values[Name: String]: String;

2.10.6. Classe THashTable


La classe THashTable mantiene una lista di oggetti. Viene utilizzato un algoritmo di
hashing per localizzare velocemente gli elementi presenti in lista.
2.10.6.1. Metodo Capacity
Capacity restituisce il numero massimo di elementi che può contenere la tabella hash.
Sintassi:
function Capacity: Integer;

2.10.6.2. Metodo Size


Size restituisce il numero di elementi presenti nella tabella hash.
Sintassi:
function Size: Integer;

2.10.7. Classe TStack


La classe TStack mantiene uno stack (pila) di puntatori.
2.10.7.1. Costruttore Create
Il costruttore Create crea una nuova lista.
Sintassi:
constructor Create;

2.10.7.2. Metodo Count


Count restituisce il numero di elementi nello stack.
Sintassi:
function Count: Integer;

2.10.7.3. Metodo Peek


Peek restituisce un puntatore all’elemento successivo in lista.
Sintassi:
function Peek: TObject;

2.10.7.4. Metodo Pop


Pop rimuove e restituisce l’elemento successivo in lista.
Sintassi:
function Pop: TObject;

2.10.7.5. Metodo Push


Il metodo Push aggiunge un elemento alla lista.
Sintassi:
procedure Push(Obj: TObject);

2.10.8. Classe TQueueList


La classe TQueueList mantiene una lista FIFO (coda) di puntatori. Da non confondere
con l’oggetto TQueue visto nel paragrafo 2.5.1!
2.10.8.1. Costruttore Create
Il costruttore Create crea una nuova lista.
Sintassi:
constructor Create;

2.10.8.2. Metodo Count


Count restituisce il numero di elementi nella lista.
Sintassi:
function Count: Integer;

2.10.8.3. Metodo Peek


La funzione Peek accede alla lista senza rimuovere elementi. Restituisce un puntatore
all’elemento successivo della lista. Ciascun oggetto TOrderedList (classe padre di
TStack e TQueueList) implementa la funzione Peek per restituire uno specifico elemento
della lista.
Ad esempio TQueueList restituisce l’elemento che era stato inserito prima, mentre
TStack restituisce l’elemento che era stato inserito dopo.
Per rimuovere un elemento dalla lista, utilizzare la funzione Pop (paragrafo 2.10.8.4).
Sintassi:
function Peek: TObject;

2.10.8.4. Metodo Pop


Pop rimuove e restituisce l’elemento successivo in lista.
Sintassi:
function Pop: TObject;

2.10.8.5. Metodo Push


Il metodo Push aggiunge un elemento alla lista.
Sintassi:
procedure Push(Obj: TObject);

2.11. Unità globale


L’unità globale contiene classi, tipi, metodi e procedure che non rientrano nelle precedenti
categorie.
CLASSE TTimeObj

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’]

Genererà un formato DateTime che rappresenta le 2 del pomeriggio di oggi.


CALCOLO DEL TEMPO
Formato 1: +HH:MM:SS
Formato 2: -HH:MM:SS
Esempi
T[‘+00:00:\30’]

Rappresenta un DateTime corrispondente a 30 secondi dopo il corrente orario.


T[‘-01:00:00’]

Rappresenta un DateTime corrispondente a un’ora fa.


WILDCARDS
Formato 1: XX:MM:SS
Formato 2: HH:XX:SS
Formato 3: HH:MM:XX
XX rappresentano caratteri jolly. La maschera avrà come valore l’orario più vicino che
valida la maschera.
Supponiamo che siano le ore 15:15, una maschera in formato XX:00:00 corrisponderà alle
ore 16:00 perché si tratta dell’orario più vicino all’orario corrente che validi la maschera.
PROSSIMA OCCORRENZA
Esempi:
NEXTHOUR
NEXT60

Entrambi restituiscono l’ora esatta successiva, ed è come utilizzare XX:00:00.


NEXTHALFHOUR
NEXT30

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;

2.11.3. Metodo CopyFile


CopyFile copia un file da una sorgente a una destinazione.
Sintassi:
function CopyFile(SrcFileName, DestFile: String; FailIfExists: Boolean): Boolean;

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;

Esempi di utilizzo del metodo T:


WriteLn(‘—Standard time’);
WriteLn(T[‘14:00:00’]); {¬## mostra le 2 di oggi pomeriggio}
WriteLn(T[‘04:30:00’]); {¬## mostra le 4 e mezza del mattino di oggi }
WriteLn(DateTime(T[‘09:00:00’]-1)); {¬## mostra le 9 del mattino di ieri}
WriteLn(DateTime(T[‘09:00:00’]+1)); {¬## 9AM TOMORROW}
WriteLn(‘—Calculation’);
WriteLn(T[‘+01:00:00’]); {¬## One hour from now}
WriteLn(T[‘-00:00:30’]); {¬## 30seconds back}
WriteLn(T[‘NEXT60’]); {¬## Next hour}
WriteLn(T[‘NEXT30’]); {¬## Next halfhour}
WriteLn(T[‘NEXT15’]); {¬## Next quarter}
WriteLn(‘—WildCards’);
WriteLn(T[‘XX:00:00’]); {¬## Same as Next Hour}
WriteLn(T[‘XX:59:30’]); {¬## 30 seconds after next minute of the hour}

WriteLn(T[‘XX:XX:30’]); {¬## Next :30 seconds}

2.11.5. Procedura WriteLn


WriteLn scrive un messaggio nella finestra di output dell’editor PAL e va alla linea
successiva della finestra di output. Questa funzione è molto comoda per il debug.
Sintassi:
procedure WriteLn(Msg: Variant);

Esempio di utilizzo di WriteLn:


{## Integers ##}
WriteLn(1);
WriteLn(1+1);
WriteStr(1); WriteStr(2); WriteLn(3);
{## Real / Float}
WriteLn(1.234);
WriteLn(1/2);
{## Strings }
WriteLn(‘My string’);
WriteStr(‘A’); WriteStr(‘B’); WriteLn(‘C’);
{## Date / Time }
WriteLn(Date);
WriteLn(Time);
WriteLn(T[‘19:15:30’]);
{## Mixed}
WriteLn(‘The time is now ‘+TimeToStr(Now));

2.11.6. Procedura WriteStr


WriteStr funziona in modo molto simile a WriteLn ma non va a capo una volta terminata
l’esecuzione.
Sintassi:
procedure WriteStr(Msg: Variant);
3. Collezione di script
All’interno di questo capitolo presentiamo una collezione di listati completi che possono
essere utili per il regista sia per comprendere le dinamiche di funzionamento della regia
che per risolvere problemi pratici comuni. Tutti gli script sono pensati per essere eseguiti
in sistemi che usino Firebird.
Per adattarsi ad altri DBMS bisogna correggere le stringhe SQL per adattarle ai dialetti del
DBMS in uso.
3.1. Come estrarre tutte le statistiche
Questo script estrae tutte le statistiche da tutti i relay impostati:
var I : Integer;
PAL.LockExecution;
for I := 0 to Relays.Count-1 do
begin
WriteStr(‘Relay numero ‘); WriteLn(I);
WriteStr(‘—Attività: ‘); WriteLn(Relays[I].Active);
WriteStr(‘—Stato: ‘); WriteLn(Relays[I].Status);
WriteStr(‘—-Ascoltatori contemporanei: ‘); WriteLn(Relays[I].Viewers);
WriteStr(‘—Picco: ‘); WriteLn(Relays[I].Viewers_High);
WriteStr(‘—Bitrate (kbps): ‘); WriteLn(Relays[I].Bitrate);
WriteStr(‘—Formato: ‘); WriteLn(Relays[I].Format);
WriteLn(‘’);
end;

PAL.UnlockExecution;

3.2. Come gestire il balance dei brani


Questo script sovrascrive i valori di balance di SAM per permettere un sistema di
attribuzione dei punteggi più preciso e accurato. Ogni volta che viene riprodotto un brano,
viene attivato lo script. La prima cosa che questo script fa è settare il balance a zero.
Vengono quindi applicate una serie di regole per ciascun brano, basate su alcuni parametri.
Ogni volta che viene violata una regola, il valore di balance di un brano viene
incrementato di un valore definito. Quando lo script ha terminato il controllo delle regole,
il brano ha un valore di balance ben definito. Più è basso il balance di un brano, più è
probabile che venga riprodotto a breve.
Normalmente, quando si utilizza una rotazione basata sulle categorie, si utilizza un
comando simile a:
Cat.QueueBottom(‘Categoria’,smWeighted, prNoRules);

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;

3.4. Come forzare le regole per i jingle e i promo


Questo script mostra come forzare le regole di rotazione della playlist per gli station ID’s.
Normalmente queste regole non possono essere forzate semplicemente perché molte
emittenti hanno pochissimi jingle in rotazione e ciò non permette di soddisfare le regole.
Questo script diminuisce i tempi minimi di ripetizione dei brani per gli station ID’s e dopo
ripristina tutto ai valori originali.
procedure PutSweeper(Category : String);
var
OldArtist, OldSong : Integer;
begin
OldArtist := PlaylistRules.MinArtistTime;

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;

3.7. Come usare PAL per trasmettere brani in un particolare momento


Questo script mostra come è possibile programmare l’inserimento in cima alla coda di un
podcast o di un brano in particolare. Nell’esempio consideriamo l’inserimento il mercoledì
alle ore 19:58.
PAL.Loop := True;
var mytime : DateTime = now;
var I : Integer;
var start_time, end_time : DateTime;
start_time := T[‘19:58:00’];
end_time := T[‘19:58:30’];
if DayOfWeek(Now) = Wednesday then
begin
if( mytime >= start_time) and (mytime <= end_time) Then
begin
WriteLn(‘Format orario’);
Queue.AddFile(‘C:\formatparte4.mp3’,ipTop);
cat[‘Station IDs (All)’].QueueTop(smRandom,NoRules);
Queue.AddFile(‘C:\formatparte3.mp3’,ipTop);
cat[‘Station IDs (All)’].QueueTop(smRandom,NoRules);
Queue.AddFile(‘C:\formatparte2.mp3’,ipTop);
cat[‘Station IDs (All)’].QueueTop(smRandom,NoRules);
Queue.AddFile(‘C:\formatparte1.mp3’,ipTop);
cat[‘Station IDs (All)’].QueueTop(smRandom,NoRules);
PAL.WaitForTime(T[‘+00:01:00’]);
end;

end;

3.8. Come far ruotare i brani in coda continuamente


Il seguente script mantiene in coda i brani trasmessi, ritrasmettendoli continuamente. Utile
per programmi vocali in cui i brani hanno come unica funzione quella di servire da base
per il parlato.
var Song : TSongInfo;
var Q : TDataSet;
AL.Loop := True;
PAL.WaitForPlayCount(1);
PAL.LockExecution;
Q := Query(‘SELECT filename FROM historylist ORDER BY date_played DESC’,[],True);
if not Q.IsEmpty then
Queue.AddFile(Q[‘filename’],ipBottom);
Q.Free;
PAL.UnlockExecution;

3.9. Come svuotare le categorie e riempirle nuovamente per effettuare un


aggiornamento
Questo script svuota un insieme di categorie e quindi le riempie nuovamente con un nuovo
insieme di brani.
PAL.Loop := True;
PAL.WaitForTime(T[‘18:00:00’]); {Attende le 6 del pomeriggio}
const Cats : String = ‘ORHP’;
var D, D2 : TDataSet;
var C : Integer;
var Current : String;
var mytime : DateTime = now;
//Cancella i brani dalle categorie
For C := 1 to Length(Cats) do
begin
Current := Cats[C];
D2:=Query(‘SELECT ID, name FROM category WHERE name = :cat’, [Current], True);
writeln (‘Cancella brani dalla categoria ‘ + D2[‘name’]);
ExecSQL(‘DELETE FROM categorylist WHERE categoryID = :categoryID’, [D2[‘ID’]]);
end;
D2.free ;
//Inizia il riempimento.
PAL.LockExecution;
For C := 1 to Length(Cats) do
begin
Current := Cats[C];
WriteLn(‘Aggiunge files a ‘+Current);
D := Query(‘select * from songlist, categorylist where songlist.id=categorylist.songID and categorylist.categoryID = 4 and UP
[Current],True);
D.First;
while not D.EOF do
begin
CAT[Current].AddFile(D[‘filename’],ipBottom);
D.Next;
end;
D.Free;
end;
PAL.UnlockExecution;
PAL.WaitForTime(T[‘23:59:59’]); {Attende l’indomani}

3.10. Come generare playlist avanzate


Lo script presentato consente di creare delle playlist che abbiano caratteristiche di
casualità maggiori rispetto a una banale estrazione di brani da categorie.
var theSongChooser1, theSongChooser2 : TDataSet;
var theArtist1, theArtist2 : String;
var theAlbum1, theAlbum2 : String;
var theGenre1, theGenre2 : String;
var duration, ls, sc1, sc2 : Integer;
PAL.Loop := True;
theAlbum1 := ‘Columbia’;
theAlbum2 := ‘Master of puppets’;
theArtist1 := ‘Andreas Vollenweider’;
theArtist2 := ‘Metallica’;
theGenre1 := ‘Pop’;
theGenre2 := ‘Rock’;
// Crea le liste di brani
theSongChooser1 := Query(‘select first 6 *’ + ‘ from songlist’ +
‘ where artist not like ‘ + QuotedStr(theArtist1) +
‘ and album not like ‘ + QuotedStr(theAlbum1) +
‘ and songtype = ‘’S’’ ‘ +
‘ order by rand()’,[],True);
theSongChooser2 := Query(‘select first 7 *’ + ‘ from songlist’ +
‘ where artist = ‘ + QuotedStr(theArtist2) +
‘ and album = ‘ + QuotedStr(theAlbum2) +
‘ or album = ‘ + QuotedStr(theAlbum1) +
‘ and songtype = ‘’S’’ ‘ +
‘ order by rand()’,[],True);

// Pesca casualmente dalle liste brani finchè non sono esaurite


theSongChooser1.First;
theSongChooser2.First;
duration := 0;
ls := 0; sc1 := 0; sc2 :=0;
// Loop
while (sc1 = 0) or (sc2 = 0) do
begin
// Stabilisce quale lista brani è esaurita
if theSongChooser1.EOF then sc1 := 1;
if theSongChooser2.EOF then sc2 := 1;
// Draw appropriate random integer to select the songlist or continue
// the selection from the last songlist
if (ls = -1) then ls := 0 else ls := RandomInt(2);
// Draw from songlist 1 if not exhausted
if (ls = 0) and (sc1 = 0) then
begin
WriteLn(theSongChooser1[‘title’] + ‘ *da* ‘ + theSongChooser1[‘album’]);
Queue.AddFile(theSongChooser1[‘filename’], ipBottom);
duration := duration + theSongChooser1[‘duration’];
theSongChooser1.Next;
end
// Increase the random number by 1 if this songlist is exhausted
else if (ls = 0) and (sc1 = 1) then
begin
ls := ls+1;
end;
// Draw from songlist 2 if not exhausted
if (ls = 1) and (sc2 = 0) then
begin
WriteLn(theSongChooser2[‘title’] + ‘ *from* ‘ + theSongChooser2[‘album’]);
Queue.AddFile(theSongChooser2[‘filename’], ipBottom);
duration := duration + theSongChooser2[‘duration’];
theSongChooser2.Next;
end
// Set the random number to -1 to signal continuation on top
else if (ls = 1) and (sc2 = 1) then
begin
ls := -1;
end;
// Draw from songlist .. if not exhausted etc.
end;
PAL.WaitForPlayCount(1);

3.11. Come trasmettere un’ora di un artista


Lo script proposto consente di trasmettere un tributo radiofonico a un singolo artista. Le
sue canzoni verranno trasmesse per un periodo di circa sessanta minuti.
var theSongChooser : TDataSet;
var duration : Integer;
var theArtist, theShow : String;
PAL.Loop := True;
if ((now >= T[‘14:00:00’])
and (now <= T[‘14:00:30’])
and (DayOfWeek(Now) = Wednesday))
or ((now >= T[‘14:00:00’])
and (now <= T[‘14:00:30’])
and (DayOfWeek(Now) = Sunday))
then
begin
if (DayOfWeek(Now) = Wednesday) then
begin
theArtist := ‘Queen’;
theShow := ‘Format1’;
end;
if (DayOfWeek(Now) = Sunday) then
begin
theArtist := ‘Metallica’;
theShow := ‘Format2’;
end;
WriteLn(‘Inizia l’’ora di ‘ + theArtist + ‘’);
WriteLn(‘’);
theSongChooser := Query(‘select first 100 * from songlist where artist = ‘ + QuotedStr(theArtist) + ‘ and songtype =
‘’S’’ order by rand()’,[],True);
duration := 0;
theSongChooser.First;
while not theSongChooser.EOF do
begin
if ( (duration + theSongChooser[‘duration’]) < 4200000 ) then
begin
WriteLn(theSongChooser[‘title’] + ‘ *da* ‘ + theSongChooser[‘album’]);
Queue.AddFile(theSongChooser[‘filename’], ipTop);
duration := duration + theSongChooser[‘duration’];
end;
theSongChooser.Next;
end;
theSongChooser.Free;
PAL.WaitForPlayCount(2);
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;

3.13. Come ritrasmettere un flusso audio in streaming


Questo script consente di trasmettere automaticamente uno show esterno a un particolare
orario, ma può essere tranquillamente applicato a un file remoto.
const ShowURL = ‘http://url:port’; // INDIRIZZO DEL FLUSSO STREAMING
const StartTime = ‘21:00:00’; // ORARIO D’INIZIO
const EndTime = ‘22:30:00’; // ORARIO DI FINE
{Imposta il giorno della settimana. L’orario impedisce che lo script faccia ripartire lo show appena finito. }
var DOW : Boolean;
DOW := (DayOfWeek(Now) = Friday) and (now < T[‘22:29:00’]);
var C : Integer;
PAL.Loop := True;
while DOW do
begin
PAL.WaitForTime(StartTime);
Queue.Clear;
Queue.AddURL(ShowURL,ipTop);
ActivePlayer.FadeToNext;
var Song : TSongInfo;
Song := TSongInfo.Create;
Song[‘artist’] := ‘Artista’;
Song[‘title’] := ‘Titolo’;
Song[‘duration’] := 90*60*1000; {90 minutes}
Encoders.SongChange(Song);
C := 1;
while C < 20 do
begin
Queue.AddURL(ShowURL,ipBottom);
CAT[‘Music (All)’].QueueBottom(smLRP,EnforceRules);
C := C + 1;
end;

PAL.WaitForTime(EndTime);
Queue.Clear;
ActivePlayer.FadeToNext;
end;

3.14. Come implementare il Two-For-Tuesday


Two for Tuesday è una strategia commerciale attuata negli States principalmente da
pizzerie e piccole attività della ristorazione, concettualmente simile al nostro “prendi uno,
l’altro è gratis”. Questo script inserisce in coda due brani dello stesso artista, soltanto nella
giornata di martedì. Se non ci sono due brani di uno stesso artista, si sceglie un altro artista
per il quale tale duplice scelta è possibile.
PAL.Loop := True;
if DayOfWeek(Now) = Sunday then
begin
var theArtistInQueue, theCountSongs, theSongChooser : TDataSet;
// Sceglie una canzone a caso e la inserisce in coda
CAT[‘Songs’].QueueBottom(smRandom,EnforceRules);
// Riconosce l’artista e l’id della canzone inserita in coda
theArtistInQueue := Query(‘select FIRST 1 title, artist, songID, queuelist.id as queueID from songlist, queuelist where songlis
[],True);
WriteLn(‘Lo script ha aggiunto: ‘ + QuotedStr(theArtistInQueue[‘artist’]));
WriteLn(‘ ‘ + QuotedStr(theArtistInQueue[‘title’]));
WriteLn(‘’);
// Contiamo il numero di brani rimanenti di questo artista
theCountSongs := Query(‘select count(*) as cnt from songlist where id <> ‘ + IntToStr(theArtistInQueue[‘songID’])
+ ‘ and songtype = ‘’S’’ and artist = ‘ +
uotedStr(theArtistInQueue[‘artist’]),[],True);
writeln(theCountSongs[0]);
WriteLn(‘Ci sono ‘ + IntToStr(theCountSongs[‘cnt’]) + ‘ da scegliere’);
WriteLn(‘’);
if (theCountSongs[‘cnt’] = 0) then
begin
WriteLn(‘Non ci sono brani da scegliere!’);
WriteLn(‘Rimuovo ‘ + QuotedStr(theArtistInQueue[‘artist’]));
Queue.Delete(StrToInt(theArtistInQueue[‘queueID’]));
WriteLn(‘Ricominciamo’);
end
else
begin
WriteLn(‘Scelgo un brano a caso’);
// Scelgo un brano a caso dai brani rimanenti di questo artista
theSongChooser := Query(‘select FIRST 1 title, filename from songlist where id <> ‘ +
IntToStr(theArtistInQueue[‘songID’]) + ‘ and songtype = ‘’S’’ and artist = ‘ + QuotedStr(theArtistInQueue[‘artist’]) + ‘
order by rand()’,[],True);
//Inserisce in coda il brano
Queue.AddFile(theSongChooser[‘filename’],ipBottom);
WriteLn(‘Aggiunto: ‘ + QuotedStr(theArtistInQueue[‘artist’]));
WriteLn(‘ ‘ + QuotedStr(theSongChooser[‘title’]));
WriteLn(‘’);
theSongChooser.Free;
theArtistInQueue.Free;
theCountSongs.Free;
WriteLn(‘Aspettiamo che vengano trasmessi 4 brani per ricominciare’);
PAL.WaitForQueue(4);
end;
end;

3.15. Come cambiare il titolo dello stream


Ci sono diversi motivi per cambiare le informazioni inviate all’encoder per visualizzare il
titolo dello stream:
♦ si sta facendo relaying di un altro flusso
♦ si vuole far visualizzare un testo promozionale o particolare.
Lo script seguente cambia il nome del flusso, leggendo il nome da un file. Effettua
periodicamente uno switch tra il testo standard e quello fornito dal file.
Ciò può prevenire il ripping dello stream da parte di chi volesse scaricare i brani trasmessi.
Per usare lo script occorre creare un file di testo semplicissimo, dove in ogni riga va
indicato uno dei messaggi da visualizzare.
{ Configurazione }
const bannertext_file = ‘C:\BannerText.txt’;
const change_interval = ‘+00:00:15’;
{ Implementazione }
var CurSong : TSongInfo;
var UpdSong : TSongInfo;
var Banners : TStringList;
var Toggle : Boolean = True;
var LinePos : Integer = 0;
PAL.Loop := True;
UpdSong := TSongInfo.Create;
Banners := TStringList.Create;
{ Carica le righe da visualizzare all’interno di un oggetto di tipo stringlist }
if FileExists(bannertext_file) then
Banners.LoadFromFile(bannertext_file)
else
WriteLn(‘Il file indicato non esiste’);
LinePos := 0;
while (LinePos<Banners.Count) do
begin
{Aspetta il tempo indicato in configurazione}
PAL.WaitForTime(change_interval);
{recupera il testo corrispondente alla posizione attuale e lo attribuisce al brano}
UpdSong[‘title’] := Banners[LinePos];
{Comunica all’encoder di aggiornare le informazioni del brano
Se il Toggle è impostato a VERO si utilizzeranno i metadata standard, altrimenti interverrà un banner inserito nel file
di testo
}
Toggle := not Toggle;
if Toggle then
begin
CurSong := ActivePlayer.GetSongInfo;
Encoders.SongChange(CurSong);
CurSong.Free;
end
else
Encoders.SongChange(UpdSong);
LinePos := LinePos + 1;
end;
Banners.Free;
UpdSong.Free;
{––––––––––––––––—}
Fabrizio Mondo
Attivo da oltre dieci anni nel settore dei media online, Fabrizio
Mondo ha dedicato la maggior parte dei suoi studi al settore delle
web radio, sia dal punto di vista tecnico che progettuale e
strutturale. Già autore del testo “Creare e gestire una web radio
professionale”, ha realizzato numerosi articoli sul mondo della
radiofonia online. Ama le arti marziali, le curiosità matematiche
e la trofie al pesto. Attualmente cura il progetto Zeptle, un
sistema di aggregazione radiofonica simile al digitale terrestre, che mira a fornire servizi
agli editori radiofonici in tutto il mondo.

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

Potrebbero piacerti anche