Dr.Godfried-Willem RAES

Kursus Experimentele Muziek: Boekdeel 1: Algoritmische Kompositie

Hogeschool Gent - Departement Muziek en Drama


<Terug naar de inhoudstafel>

1130:

TIJD en RITME

In sommige van onze eerste tot het uiterste vereenvoudigde programmavoorbeelden hebben we wellicht wel eens -op instigatie van de vele elementaire en eigenlijk slechte Basic boekjes waarmee de markt overspoeld is- gebruik gemaakt van wat men een 'loze lus' noemt om een bepaalde toestand (het klinken van een noot, een rustpause o.i.d.) gedurende een zekere tijd aan te houden.

Voorbeeld:

DECLARE SUB Uit (byte%)

Uit 144

Uit 60

Uit 127

FOR X = 0 TO 1000

NEXT X

Hoewel de FOR-NEXT lus in bovenstaand voorbeeld min of meer werkend te krijgen is, kan het toch gelden als een verfoeilijke wijze van programmeren. Deze 'truuk' heeft ingang gevonden omdat in de prehistorie van de personal komputers (de tijd van de Sinclair-Timex ZX81, de TRs-reeks van Tandy, de Apple II, de VIC...) geen meer gesofistikeerde mogelijkheden tot tijdskontrole bestonden, althans niet, zonder gebruik te maken van machinetaal. Bezwaren die aan deze werkwijze kleven zijn:

Kunnen de vorige bezwaren nog ondervangen worden door in onze software bvb. een procedure te voorzien die de tellengte afhankelijk maakt van een geautomatiseerde meting van de snelheid van de komputer, dan is volgend bezwaar in elk geval onoverkomelijk en veel zwaarder:

BASIC-Timing bij IBM-PC's en hun klonen:

De <SOUND x%,y%> instruktie

De eerste drie bezwaren kunnen bij gebruik van een IBM-PC en Quick-Basic (QB4.5 of QBX V7.1) worden opgevangen door het gebruik van instrukties zoals:

SOUND 20000, tijd%

Op de IBM-PC houdt deze instruktie in dat de komputer een toon van 20kHz produceert via het ingebouwde luidsprekertje. Aangezien deze toonhoogte niet door het speakertje kan worden weergegeven en zelf als zou dat wel het geval zijn, deze toon hoe dan ook niet door ons kan worden gehoord, is het effekt van deze instruktie dat we een wachttijd verkrijgen in verhouding tot de grootte van de variabele tijd. Deze variabele moet een integer zijn, zoniet krijgen we een foutmelding. Het grootste bezwaar dat kleeft aan dit gebruik van de SOUND instruktie is dat het oplossend vermogen bijzonder klein is voor ernstige muzikale toepassingen. Immers de duur van één eenheid tijd in de SOUND instruktie bedraagt 1/18.2 sekonde. Bovendien kan, zoals gezegd, alleen met gehele getallen worden gewerkt. Dit maakt het implementeren van een geleidelijk accelerando in muziek volkomen onmogelijk.

Ook moet opgemerkt dat de komputer niet stopt voor de duur van de produktie van de in dit geval onhoorbare klank, maar met de volgende instruktie doorgaat tijdens de duur van de klank, tenzij dit een nieuwe SOUND instruktie is. In feite gebeurt hier een heel primitieve vorm van multitasking. De SOUND-buffer wordt volledig leeggemaakt door de instruktie SOUND toonhoogte, 0 op te geven.

De <SLEEP x> instruktie

Een andere instruktie, die het bezwaar van het geringe oplossend vermogen in nog sterkere mate kent is

SLEEP tijd%

Voor deze instruktie moet tijd niet alleen een geheel getal zijn, maar is de kleinste waarde een volle sekonde!

De SLEEP instruktie is vooral nuttig wanneer we bepaalde beelden of berichten op het scherm gedurende enige tijd willen vasthouden opdat ze gelezen zouden kunnen worden door de gebruiker. Wanneer een toets wordt ingedrukt voordat de opgegeven tijd is verstreken, gaat het programma gewoon door met de eerstvolgende instruktie.

De <INKEY>- loop metode

Maar, ook in dergelijke toepassingen gaat de instruktie na korte tijd op de zenuwen werken van de gebruiker. Beter is het dan, de duur van een schermbericht afhankelijk te maken van de gebruiker zelf, door hem bvb. een willekeurige toets te laten indrukken alvorens naar een volgende stap in het programma over te laten gaan. In een programmavoorbeeldje uitgedrukt:

' maak eerst de buffer leeg (clear keyboard buffer):

DO

LOOP UNTIL INKEY$=""

.

' wanneer een toets ingedrukt wordt verlaat het

' programma volgende lus:

DO

LOOP UNTIL INKEY$ <> ""

De <TIMER> funktie:

Een volgende, andere, en wellicht de meest handige, mogelijkheid waarover we op IBM-achtige machines binnen BASIC kunnen beschikken om redelijk precieze tijdsmetingen uit te voeren is de TIMER instruktie.

Volgend statement retourneert het aantal sekonden (bij default als single precision reeel getal (!) maar door forcering wel enkele decimalen op te drijven via een double precision deklaratie van een variabele (#)) dat verlopen is tussen het aanzetten van de machine en het moment waarop de instruktie wordt uitgevoerd:

A#=TIMER

Een tijdsmeting kan eenvoudig geschieden alsvolgt :

STARTTIME#=TIMER

. hier komen programmaregels en

instrukties die een zekere tijd in beslag nemen

.

STOPTIME#=TIMER

DUUR#=STOPTIME#-STARTTIME#

Een uitgewerkt voorbeeld waarin dit werd toegepast ist terug te vinden in de programmamodule TIMTST bij het programma voor de uitvoering van "Crossings" van Alvin Lucier. (cfr. inhoudstafel)

Wanneer we een gegeven tijdsduur willen laten verlopen kan volgende konstruktie -als procedure- gebruikt worden:

DECLARE SUB Wacht (byte%)

Wacht (25) (in millisekonden)

SUB Wacht (b%)

tmptim!= b% /1000

now!=TIMER

DO : LOOP UNTIL TIMER - now! >= tmptim!

ENDSUB

Of, efficienter geprogrammeerd:

SUB Wacht (b%)

over! = TIMER + (b% / 1000!)

DO : LOOP UNTIL TIMER >= over!

ENDSUB

Dit loopt eksakter en efficienter omdat de aftrekking buiten de DO-LOOP werd gehouden en dus niet elke keer opnieuw moet berekend worden.

Willen we gebruik maken van gebruikers-vriendelijke input in onze programmas dan kunnen we, in muziekprogrammas waarbij we een tempo willen realizeren, ook hier uitgaan van een op te geven metronoom-getal MM% (integer).

De in het programma te gebruiken teleenheden, uitgaand van bvb. de tweeendertigste noot als kleinste metrische waarde, wordt dan gegeven door:

T! = 60/ (MM% * 8)

Ingebouwd in een programmastruktuur kunnen we dan elke kleinste metrische onderverdeling aftellen alsvolgt:

T! = 60 / (MM% * 8)

.

Tim!= TIMER

. instruktie die noten speelt o.i.d.

DO UNTIL TIMER - Tim! >= T!

LOOP

Willen we bvb. een klank een gepunte vierde aanhouden, dan kan dit door eenvoudigweg T in de lus te vermenigvuldigen met het aantal tweeendertigste noten in een gepunte vierde noot. Dus:

Tim! = TIMER

. noot-aan

DO UNTIL TIMER - TIM! >= T! * 12

LOOP

Met de behandeling van de Timer-instruktie hebben we zowat het onderste uit de kan gehaald van wat zonder enige kennis van de machinearchitektuur en binnen vertrouwde Basic-versies haalbaar is.

In een volgende paragraaf gaan we verder in op enkele mogelijkheden om tijdinformatie rechtstreeks uit de hardware af te leiden, maar eerst willen we toch een Basic instruktie vermelden die even nuttig als gevaarlijk is bij het afhandelen van wacht-tijden in een programma:

Naast deze TIMER-funktie kent Basic ook een ON TIMER interrupt mogelijkheid. (Met de daaraan gekoppelde instrukties TIMER ON/OFF). Dit op zich erg mooi taalelement blijkt echter voor muzikale toepassingen onbruikbaar, omdat het alleen voor veelvouden van gehele sekonden kan worden gebruikt. We gaan er dan ook niet nader op in.

De <WAIT> Instruktie.

Deze instruktie laat het programma toe te kijken naar een welbepaald poortadres (in het I/O bereik dus), op dat adres de waarde (een byte) te vergelijken met eén of twee parameters (masks) en wanneer de opgegeven overeenkomst waar wordt bevonden, verder te gaan met de afhandeling van het programma.

De syntaks luidt:

WAIT I/O-adres%, byte1%, byte2%

waarbij:

I/O-adres: adres van de te volgen I/O-poort

byte1%, byte waarmee de ingelezen waarde met een

booleaanse AND operatie wordt vergeleken en geevalueerd

(0=false, niet-0=waar)

byte2%, byte dat (optioneel) kan worden opgegeven en

waarmee tegenover het te lezen byte eerst een Booleaanse

XOR-operatie wordt uitgevoerd, vooraleer de AND uit

te voeren.

Wanneer het rezultaat van de WAIT instruktie nooit waar wordt, dan betekent deze instruktie wachten op Godot..., en kunnen we alleen nog naar de hardware reset-knop reiken. Daarmee is dan ook het gevaar verbonden aan deze instruktie aangetoond.

Het voordeel ervan, is dat het een van de snelste en meest akkurate instrukties is die binnen Basic werden geimplementeerd. Het -in heel wat tragere kode- vertaalde equivalent ervan is:

DO

b%=INP(IOadres%)

LOOP UNTIL (b% XOR byte2%) AND byte1%

Toepassingen van het WAIT-kommando in onze eigen programma's zijn legio: kijk bvb. naar de procedures die we gebruiken voor het versturen en/of ontvangen van Midi-bytes...

Wie met Visual Basic wil werken, zal het zonder deze instruktie moeten stellen. VB is nu eenmaal een kreupele programmeertaal.


Terug naar inhoudstafel kursus: <Index Kursus>

Naar volgende paragraaf: <1132>

Naar homepage dr.Godfried-Willem RAES