Dr.Godfried-Willem RAES

Kursus Experimentele Muziek: Boekdeel 1: Algoritmische Kompositie

Hogeschool Gent

Departement Muziek en Drama


Terug naar de inhoudstafel

 

1060: SUBROUTINES PROCEDURES PROGRAMMAMODULES

 

 

1.Elementaire Basic's

Veel voorkomende en op elkaar gelijkende stukken uit een programma of algoritme kunnen zelfs in de meest elementaire Basic versies met profijt in een SUBROUTINE worden geplaatst. Het verdient aanbeveling vaak gebruikte subroutines afzonderlijk als programmastukjes op diskette op te slaan (te 'saven' ). Je kunt ze voor andere programmas steeds weer hergebruiken door ze met het nieuwe programma te verbinden. In oudere Basic-versies gebruik je hiervoor de Basic instruktie MERGE. (Zoek zelf de syntax op). Je moet wel je programmafiles saven als ASCII-bestanden. (Save/a optie). In modernere versies kan je het bestand via een gewoon ‘load’ kommando in het file-menu opladen.

Het volgende voorbeeld maakt gebruik van subroutines met de GOSUB -RETURN kommandos en vergemakkelijkt de uitsturing van een midi-sekwens aanzienlijk :

MAIN:

N1 = algoritmische uitdrukking waarin de te spelen noot wordt berekend ...

T1 = algoritmische uitdrukking waarin de duur wordt berekend

...

N=N1 : T=T1 : GOSUB MION : GOSUB TIJD

N2 = algoritmische uitdrukking noot...

T2 = algoritmische uitdrukking tijd...

...

N=N2 : T=T2 : GOSUB MION : GOSUB TIJD

N3 = ...

...

...

N=Nx : T=Tx : GOSUB MION: GOSUB TIJD

N=Ny : GOSUB MIOFF

END

MION: ' Via MPU401 - IBM/ISA sub

Uit 144: Uit N: Uit 64

RETURN

MIOFF: 'midiroutine note OFF

Uit 144: Uit N: Uit 0

RETURN

TIJD: 'timing routine

tl%= TIMER + T

DO: LOOP UNTIL TIMER > tl%

RETURN

 

Wanneer een programma een op zichzelf staand geheel gaat vormen dat je ook later in andere konteksten of voor andere stukken wilt kunnen gebruiken, dan kan je die delen in vele eenvoudige Basics ook vanuit een ander programma oproepen.

Stel dat een programma werd geschreven waarin alle parameters voor het programmeren van een midi-FM-synthesizer worden gestuurd dan kan dit programma in een nieuw programma opnieuw worden gebruikt. Stel dat je het naar disk wegschreef onder de naam "progr001.bas". (Denk erom dat dit wegschrijven in nagenoeg alle Basics dient te gebeuren in de vorm van een ASCII-bestand !. Dit wordt bereikt met de optie SAVE "filenaam",A., of in QBX/BC7 door het aanklikken van het betreffende vakje bij het SAVE-menu )

Voorbeeld :

10 ' nieuw programma

...

999 'einde nieuw programma

1000 RUN "progr001.bas"

 

Niet alle Basics laten dit echter toe. Soms (-in dit geval op een komputer die vandaag als welhaast voorhistorisch mag gelden, met name de allereerste laptopkomputer: de HX20 van Epson) moet je het doen alsvolgt:

999 TITLE "Nieuw"

1000 LOGIN "progr001"

2000 LOGIN "Nieuw"

'(Epson HX20 kode )

of nog , op de intussen al even achterhaalde MSX-komputertjes:

 

1000 LOAD "progr001.bas",R

 

maar dan kan je niet naar het nieuwe programma weerkeren. Steeds bestaat echter de ultieme mogelijkheid het programma te MERGE 'n . Draag er dan echter wel zorg voor dat het bereik der regelnummers (bij gebruik van oude Basic versies) der beide programmas ver uiteen ligt. Zoniet worden de regels overschreven met die van het te MERGE'n programma. Voor modernere Basic versies moet je eerst nagaan of de gebruikte variabelen wel kompatibel zijn en of er geen identieke labels of gelijknamige procedures (SUB's) en funkties worden gebruikt.

Voor dit soort manipulaties leent een modern en gecompileerd Basic zich uiteraard stukken beter, maar het is niet voor de oudere machines beschikbaar. Daarover verder echter meer... We houden eraan zo af en toe toch eens de verwijzen naar de gang van zaken op oudere toestellen, niet in het minst omdat je ze als je een klein beetje moeite doet gemakkelijk gratis kunt krijgen (ze leven alleen nog op rommelzolders en opberghokken) en omdat dergelijke tuigen voor hardware experimenten en machinebesturingen vaak nog uitstekende diensten kunnen bewijzen.

 

GOSUB Toepassingsvoorbeeld:

Primitieve subroutines voor tijdsduur en ritme

De programmastruktuur die we in amateuristische programmas vaak aantreffen -en we gaven er ook zelf een voorbeeld van- als metode om een kleine tijdsduur vast te leggen via een loze tellerlus (for x=0 to 1000: Next x) heeft nog afgezien van het stuntelige ervan, ook wel nogal wat ernstige bezwaren. In de eerste plaats maakt het laten tellen van de komputer, de beoogde tijdsduur volkomen afhankelijk van de snelheid van de machine zelf. Wanneer we eenzelfde op die wijze geprogrammeerd stukje op een andere machine laten lopen, kan het resultaat erg verschillend zijn. Machinesnelheden lopen erg sterk uiteen (van 1 tot het 500-voudige zelfs).

Volgende subroutine komt tegemoet aan dit euvel omdat gebruik wordt gemaakt van de ingebouwde real-time clock die in zowel de IBM-machine als zelfs in de Atari gebruikt wordt voor de SOUND instruktie.

(Wat betreft Atari: Ik vond geen direkte opdracht in Atari STBasic om dit te realizeren - wat n.b. niet wil zeggen dat het niet zou kunnen - maar GFA-basic doet het alleszins behoorlijk ):

 

5000 'Atari 1040 timer-subroutine via sound

5001 TIM=T *12

5002 IF TIM>255 THEN TIM=255

5003 SOUND 1,0,0,0,TIM

5004 RETURN

Deze subroutine geeft als kleinste tijdseenheid 0.25 sec. Muzikaal dus zestienden bij vierde noot=M.M.60. Het aantal eenheden hiervan moet vooraleer de subroutine wordt gebruikt in T worden vastgelegd. TIM mag alle waarden aannemen tussen 0 en 255. Een eenheid komt overeen met een vijftigste van een sekonde. ( cfr. Atari-basic ST manual p. C-132). Hierover echter meer in een verdere paragraaf, speciaal over tijd en ritmiek in programmastrukturen.

 

 

 

2.Geavanceerde Basic's

In alle moderne Basic implementaties (Basic's die kompileren en zonder regelnummers funktioneren) zijn er inzake subroutines en programmamodules veel verder ontwikkelde mogelijkheden.

Zo bestaat de mogelijkheid zogenaamde 'PROCEDURES' te gebruiken. Dit zijn autonome programma-gehelen waarbinnen variabelen slechts lokale betekenis kunnen hebben (tenzij ze expliciet worden overgedragen), en die geheel los van het hoofdprogramma ontwikkeld en getest kunnen worden. Een procedure krijgt een naam of label, en kan met die naam worden opgeroepen. Binnen een programma gedragen ze zich zo'n beetje als extenties van Basic zelf. In GFA- Basic gaat zoiets alsvolgt:

 

... hoofdprogramma ...

GOSUB procedurenaam (lijst over te dragen parameters)

... vervolg hoofdprogramma

 

De procedure ziet eruit als :

PROCEDURE procedurenaam (lijst met te ontvangen variabelen)

LOCAL lijst variabelen met uitsluitend lokale betekenis

. .. subprogramma ...

ENDPROC of RETURN

 

In Microsoft Quickbasic evenals in het professionele QBX-BC7 kan het zelfs nog een stuk eleganter, omdat daar de subprogrammas (procedures en funkties eigenlijk) in de listing van het hoofdprogramma de struktuur niet meer kunnen verdoezelen. Hier moeten evenwel alle verder door het hoofdprogramma gebruikte modules,procedures en funkties, bij het begin van het programma worden opgesomd (gedeklareerd). Dat gaat dan alsvolgt:

'begin hoofdprogramma (MAIN-module)

CONST (eventuele konstanten. Deze zijn steeds en automatisch shared)

COMMON SHARED (eventuele gemeenschappelijke variabelen)

DECLARE SUB <naam procedure 1> ( over te dragen variabelen)

DECLARE SUB <naam procedure 2> ( over te dragen variabelen)

DEFINT A-Z

... programma zelf ...

CALL <naam procedure 1> (over te dragen variabelen)

... programma ...

CALL <naam procedure 2> (over te dragen variabelen)

... programma

END

 

'eerste subprogramma (procedure)

SUB <naam procedure 1>(over te dragen variabelen)STATIC:

' static wordt alleen opgenomen wanneer de procedure de waarde van haar eigen variabelen tussen de diverse oproepen dient te bewaren. Dit kost je wel heel wat stack-geheugen. Daarom bestaat ook de mogelijkheid te specifieren welke variabelen tussen de 'calls' moeten bewaarde worden. De syntax is dan:

STATIC (lijst variabelen)

Is er slechts een of enkele niet bij voorbaat als common shared gedeklareerde variabelen nodig in de procedure, dan kan je die aan de procedure bekend maken alsvolgt:

SHARED (lijst variabelen)

...

END SUB

 

'tweede subprogramma (procedure)

SUB <naam procedure 2>(variabelenlijst) STATIC:

...

END SUB

 

In Quickbasic QB4.5 en QBX V7.1 is het zelfs mogelijk een verzameling van deze procedures en funkties in een afzonderlijke programmamodule onder te brengen en deze dan afzonderlijk te kompileren en ze op te nemen in een zogenaamde bibliotheek (Library). Wanneer men dan een nieuw programma maakt hoeft men het te gebruiken en reeds in de bibliotheek opgenomen procedures en funkties niet eens meer in het programma op te nemen. Een simpele Call instruktie volstaat, wanneer men tenminste niet vergeet om bij het kompileren de juiste library-file aan het programma op te geven. (Je moet in BC7 bvb. Basic opladen met de extentie C:\bc7\QBX /L <library file>. Je mag wel niet vergeten de procedures in de library in je programma te declareren via een include-file. Dit is een gewoon ASCII-bestand met de declaraties van de procedures en funkties opgenomen in de library. Deze werkwijze is -terloops gezegd- volkomen identisch met die bij programmeertalen zoals C, C++, Fortran enz...

 

Het zal duidelijk zijn dat dergelijke mogelijkheden het efficient en gestructureerd programmeren heel sterk kunnen bevorderen.

Aanbevolen moet worden de desbetreffende hoofdstukken in de Basic Manual (Programmers Guide of iets dergelijks) heel grondig te bestuderen. Het is immers niet de bedoeling dat deze kursus een handleiding zou worden voor het gebruik van allerlei Basic implementaties, wel om er de praktische toepasbaarheid van te demonstreren. Zonder grondige en autonome studie van de manuals, kom je inzake komputermuziek en algoritmische kompositie nergens.

Bestuderen van bestaande en werkende programmas -mits deze zijn gedokumenteerd!- is ook erg leerzaam. Van het gebruik van programmas waarvan je geen toegang hebt tot de bronkode valt niets te leren. Daarom is het in akademische en wetenschappelijke kringen een substantieel deel van de etiek om de bronkode vrij beschikbaar te maken. Bovendien kan ook slechts op deze wijze vooruitgang worden geboekt: er kan dan immers verder worden gebouwd op konstrukties opgezet door anderen.

 

3.Opmerking:

 

Wie thuis of op kot (voorlopig nog) niet over MIDI-apparatuur beschikt kan vele algoritmische oefeningen uit deze kursus ook voorlopig uittesten via de sound instruktie die ook de meest eenvoudige huiskomputers ingebouwd hebben. Muzikaal gezien zal een en ander echter niet bepaald overtuigend klinken.

Bij Atari hebben we de beschikking over volgende parameters :

 

SOUND register,volume,noot,oktaaf,duur

en volgende waarden: 1-3 ,0-15 ,1-12, 1-8 , 0-255

vb.variabelen: CH VOL NT OK DUR

Als je MIDI nootnummer gelijk is aan NOOT ( variabele) dan kan je dit eenvoudig omzetten in nootnummer en oktaafwaarden alsvolgt:

NT= 1+(NOOT MOD 12)

OK= NOOT \12

Wanneer je midikanalen gebruikt, dan kan je dit omzetten in registers door er gewoon 1 bij op te tellen. Dus:

CH= K +1

Let op echter, want de sound instruktie kan maximaal driestemmig gebruikt worden, en elk register (bruikbaar als pseudo-midikanaal) is monofoon. De aanslagsterkte informatie (3e midi-byte) kan ook erg eenvoudig worden omgezet naar voor de SOUND instructie geschikte waarden alsvolgt:

VOL = V\9

De tijdsduur, zoals we reeds zagen, behoeft in dit geval niet eens een afzonderlijke subroutine, want ze is ingebouwd in de sound instruktie. Ze kan in stukjes van 1 vijftigste sekonde worden geprogrammeerd.

Ook het WAVE kommando kan gebruikt worden voor klankopwekking (alleen op de Atari ST). Met dit kommado kunnen enkele golfvormen worden ingesteld evenals de omhullende van een klank, een mengfunktie, de periodetijd voor de omhullende en ook nog eens de tijdsduur.

Naast de Atari hebben ook de Tandy 1000 en vele MSX machines eenzelfde klankchip ingebouwd. Het gaat hier om een chip met typenummer AY-3-8910A van General Instruments. Hij wordt echter steeds met andere instrukties binnen Basic aangestuurd. Alle machines die deze chip hebben, hebben ook op muzikaal vlak precies dezelfde mogelijkheden. Uitbreidingskaarten met een dergelijke chip kunnen makkelijk worden opgebouwd en aangesloten op de printeruitgang van bijna elke denkbare komputer. Ik heb er in 1983 zelfs eens een kleine FM-synthesizer mee gebouwd (de 'Synthelog V').

 


Terug naar de inhoudstafel

Naar nuttige midi-procedures

Naar midi-standaard

Naar homepage Dr.Godfried-Willem RAES