Prof.Dr.Godfried-Willem RAES

Kursus Experimentele Muziek: Boekdeel 3: Vormleer

Hogeschool Gent : School of Arts


<Terug naar inhoudstafel kursus>

   

3114:

Spektrale Harmonie

In de vorige hoofdstukjes behandelden we de mogelijkheid en konstruktie van harmoniesystemen met een symmetrie rond een gegeven tonaal centrum.

Hier willen we een volkomen ander uitgangspunt behandelen: namelijk de mogelijkheid harmoniesystemen te ontwerpen waarbij uitsluitend wordt uitgegaan van de spektrale eigenschappen van een gegeven te harmonizeren noot tegenover een gegeven tonaal centrum.

Ook hiervoor schreven we een komputerprogramma:

' **************************************************************************

' * Spectral Counterpoint Nr.1 *

' * CLIMBING *

' * Up to 7 Voices *

' * author: Dr.Godfried-Willem RAES *

' * SOURCE FILE= KORSPEK3.BAS *

' **************************************************************************

' Rules:

' A scale consists of notes belonging to the rising spektral series. The program needs user input in order to build this scale: It calculates the minimum number of harmonics to take into consideration (1 to 27 : multiplier of the fundamental note Tc% All chords consist of NrVoices% different notes belonging to this scale.There are no doublings in the chords, regardless the number of voices generated.

' Chords are only possible on notes belonging to the scale Notes to be harmonized and not belonging to the scale will cause modulation, such that the note belongs to a new scale as close as possible to the original scale. All propulsive chords have to be solved.A note belonging to the fundamental scale shall never give rise to modulations.Remark: if scales are based on 27 harmonics or more, then we get a 12-tone scale in which there will be no modulation at all!!!At the other side, we need a minimum of 7-harmonics in order to guarantee 4-part harmony without doublings. Hence the limitations in the menus.There are -as yet- 6 different chords possible.

' The program uses either real-time input through midi, or, any *.BIN file in the Raes P() format. Against the voice or midichannel chosen from this input file the requested number of parts will be composed in counterpoint. These parts can be placed either above, around or below the given voice.Note-ranges for the voices should be given by the user.

REM $DYNAMIC

CONST Tit1$ = "Spectral Counterpoint"

CONST Tit2$ = "Real Time Parametric Multivariable Harmony Algorithm"

CONST Tit3$ = "Version V1.3"

CONST Ver$ = "Music Composition Software by Dr.Godfried-Willem RAES 1996 [V1.3]"

CONST Tit4$ = "Running in Real Time Mode: <ESC>-key to Quit program"

CONST La = 440! ' in our convention, variables without explicit

CONST False = 0 ' type, are always declared constants.

CONST True = NOT False ' (except for strings)

CONST KleurScherm = True

CONST Dp = &H330 ' Midi card port adress - IRQ not used

CONST Sp = Dp + 1

CONST MidiIrq = 11 ' needed for disabling it!

CONST Debug = False: ' change to 0 before compilation!

CONST MidiBuffer = &H1FF ' size for midi in fifo

' Shared variables:

COMMON SHARED RealTim% ' Logic (True or False)

COMMON SHARED Merging% ' Logic

COMMON SHARED RTSource% ' 0 = midi-input, 1 = egg-input, 2 = bom-input

COMMON SHARED Gronddo! ' frequency of midi note 0

COMMON SHARED Tc% ' tonal center

COMMON SHARED Rubberbanding% ' modulation style parameter

COMMON SHARED i& ' note-counter

COMMON SHARED NrHarms% ' number of harmonics to build scale from

COMMON SHARED NrNotes% ' number of different notes in the scale

COMMON SHARED NrVoices% ' number of voices for the piece

COMMON SHARED NrTrans% ' aantal mogelijke transposities van Tc%

COMMON SHARED HPointer% ' pointer to HarmSeries%()

COMMON SHARED SolveTo%

COMMON SHARED MidiInChannel% ' Channel 0-15 to take as input if real-time

COMMON SHARED FifoWritePtr% ' for midi RT-implementation

' FifoReadPtr% can be static in Getnote%

' Shared Arrays:

COMMON SHARED P%(): ' score array

COMMON SHARED Stem%() ' voice order array

' Stem%(0): given voice (= 1,3,5,7...)

' Stem%(1): added voice 1 (= odd and <> Stem%(0), <> Stem%(2) )

' Stem%(2): added voice 2 (= odd and <> Stem%(0), <> Stem%(1) )

' Stem%(3): added voice 3 (= odd and <> Stem%(0),<>Px%.<>Stem%(2))

' etc...

COMMON SHARED Vhigh%(), Vlow%()

' Vhigh%(1), Vlow%(1): ' range for added voice 1

' Vhigh%(2), Vlow%(2): ' range for added voice 2

' Vhigh%(3), Vlow%(3): ' range for added voice 3

' etc..

COMMON SHARED Ladder%() ' derived scale

COMMON SHARED HarmSeries%() ' scale in harmonic order

COMMON SHARED OldHarmSer%() ' temporary array for modulations

COMMON SHARED VorigeNoot%()

COMMON SHARED VorigeVelo%()

COMMON SHARED VolgendeNoot%()

COMMON SHARED NuNoot%() ' de te harmonizeren noot

COMMON SHARED NuVelo%()

COMMON SHARED LadMat%() ' transposition matrix

' dimension 1 = 0 to NrTranspositions

' dimension 2 = 1 to Nr of notes

' This array contains all possible harmonic scales.

COMMON SHARED Fifo%() ' Midi-input buffer for RT implementation

' Procedure declarations:

DECLARE SUB Kleur (k%) ' sets screen colors if constant is set

DECLARE SUB Logo () ' program header for screen

DECLARE SUB Blank1059 () ' wipes out display lines 10 to 59

DECLARE SUB Getparam1 () ' user parameter interface

DECLARE SUB GetToneSystem ()

DECLARE SUB GetMatrix () ' returns transposition matrix LadMat%()

DECLARE SUB GetVoicing ()

DECLARE SUB GradeBotChord () ' writes a chord on the lowest harmonics

DECLARE SUB GradeSubChord () ' writes a chord with harmonics below the

' given note harmonic grade

DECLARE SUB GradeCenChord () ' writes a chord spread arount the given

' note and closely connected to it.

DECLARE SUB GradeAbochord () ' writes a chord with harmonics above the

' given note harmonic grade

DECLARE SUB GradeTopChord () ' writes a chord using only the highest partials

DECLARE SUB Showsit ()

DECLARE SUB ShowMatrix () ' displays tonesystem & transpositionmatrix

DECLARE SUB Rbin ()

DECLARE SUB Uit (byte%)

DECLARE SUB Mpuuart ()

DECLARE SUB Mplay ()

DECLARE SUB Melodize ()

DECLARE SUB Binder ()

DECLARE SUB RTbinder ()

DECLARE SUB Writfil ()

DECLARE SUB Maakladder (k%)

DECLARE SUB TestChords ()

DECLARE SUB Midi (byte%)

' Function declarations:

DECLARE FUNCTION NoteFreq! (byte%) : ' converts midi-notes to frequency

DECLARE FUNCTION FreqNote% (frq!) : ' converts frequency to nearest note

DECLARE FUNCTION HaalWijzer% (k%) : ' returns pointer to harmseries()

DECLARE FUNCTION HarmSize% () : ' returns the minimum number of

' harmonics to build a scale from

DECLARE FUNCTION GetNote% () ' read a note from the midibuffer

' Sizing of arrays:

DIM SHARED P%(0 TO 100, 0 TO 15) ' provisory

DIM SHARED Stem%(0 TO 6): ' 7 voices is the maximum for P%() format

DIM SHARED Vhigh%(0 TO UBOUND(Stem%)) ' voorlopige dimensioneringen

DIM SHARED Vlow%(0 TO UBOUND(Stem%))

DIM SHARED Ladder%(1 TO 12)

DIM SHARED HarmSeries%(1 TO 12)

DIM SHARED OldHarmSer%(1 TO 12)

DIM SHARED VorigeNoot%(0 TO UBOUND(Stem%))

DIM SHARED VorigeVelo%(0 TO UBOUND(Stem%))

DIM SHARED VolgendeNoot%(0 TO UBOUND(Stem%)): ' we use only the 0-dimension

DIM SHARED NuNoot%(0 TO UBOUND(Stem%)): ' = P%(i&,Stem%(0))

DIM SHARED NuVelo%(0 TO UBOUND(Stem%))

DIM SHARED LadMat%(1 TO 12, 1 TO 12): ' provisory dimension

DIM SHARED Fifo%(0 TO MidiBuffer): ' for RT-mode with midi

' Arrays only used in the main-module:

DIM OldNoot%(0 TO 15)

DIM OldVelo%(0 TO 15) ' only needed for midi-out in RT-mode.

' ***************************** PROGRAM START *******************************

SCREEN 12: WIDTH 80, 60 ' VGA support only

Debuglevel% = 10 ' met deze variabele kunnen

' afzonderlijke procedures uitgetest worden.

' set to 10 for file-mode

SolveTo% = False

Merging% = False

RealTim% = False

Mpuuart

' eenmalige berekening van de frekwentie voor

' midi noot 0 (dit is de laagste do, ca. 8Hz)):

Gronddo! = (La * (2! ^ (3! / 12!))) / 64

CLS

Logo

Getparam1

IF Merging% = False THEN

IF RealTim% = False THEN

' in dit geval moeten we alle noten behalve

' die van de te harmonizeren stem, wissen!

FOR i& = 0 TO UBOUND(P%, 1)

FOR j% = 1 TO 13 STEP 2

IF j% <> Stem%(0) THEN

P%(i&, j%) = False: P%(i&, j% + 1) = False

END IF

NEXT j%

NEXT i&

END IF

END IF

GetVoicing

GetToneSystem

GetMatrix

Logo

Blank1059

ShowMatrix

' Main Loop for File-input condition: **************************************

i& = 0 ' reset loop counter

NuNoot%(0) = P%(i&, Stem%(0)): ' initialization

NuVelo%(0) = P%(i&, Stem%(0) + 1)

TcCount% = 0 ' reset modulation counter

FifoWritePtr% = 1 ' otherwize it never starts...

DO

' look for the note to be harmonized:

ChordRequest% = False

IF RealTim% = False THEN

' File-mode case:

IF (P%(i&, Stem%(0)) > 0) AND (P%(i&, Stem%(0) + 1) > 0) THEN

ChordRequest% = True

' look for the next note to be harmonized:This has no function in this type of

' harmony. Modulations will not be anticipated here. In a real-time application the next

' note belong to unknown future anyway...

NuNoot%(0) = P%(i&, Stem%(0))

NuVelo%(0) = P%(i&, Stem%(0) + 1)

ii& = i& + 1

DO

Nn% = P%(ii&, Stem%(0))

IF (Nn% > 0) AND P%(ii&, Stem%(0) + 1) = False THEN

Rest% = True

EXIT DO

ELSE

Rest% = False

END IF

ii& = ii& + 1

LOOP UNTIL (Nn% > 0) OR (ii& > UBOUND(P%, 1))

VolgendeNoot%(0) = Nn% MOD 12:

' tonale graad van de opvolger t.o.v. Tc% Komt zij voor in de ladder?

' Wanneer Rest% waar is, is de volgende noot een rust.

' In dit geval moet eigenlijk een of andere vorm van kadens of tonale bevestiging

' geschreven worden.We beperken ons hier tot het schrijven van een spektraal grondakkoord:

IF Rest% AND (SolveTo% = False) THEN

SELECT CASE Rubberbanding%

CASE 0

SolveTo% = 1: ' doesn'nt change Tc%

CASE 1

SolveTo% = 1: ' schrijf een grondakkoord bij ingang van

' de volgende noot en verschuif Tc%

' 1 stapje terug

IF TcCount% > 0 THEN TcCount% = TcCount% - 1

FOR ip% = 1 TO NrNotes%

HarmSeries%(ip%) = LadMat%(TcCount%, ip%)

NEXT ip%

Tc% = LadMat%(TcCount%, 1)

CASE 2

SolveTo% = 1: ' schrijf een grondakkoord bij ingang van

' de volgende noot en reset ook Tc%

TcCount% = 0

FOR ip% = 1 TO NrNotes%

HarmSeries%(ip%) = LadMat%(TcCount%, ip%)

NEXT ip%

Tc% = LadMat%(0, 1)

END SELECT

END IF

ELSE

ChordRequest% = False

END IF

ELSE

' Real-Time mode case:

' ********************

IF RTSource% = 0 THEN Midi -1

' dummy call the midibuffer-procedure this reads input and handles output at

' the same time! We send out a dummy byte! (this reads midi)

' voorziening voor Real-Time kontrapunt: Te te schrijven procedure moet in de

' midififobuffer kijken en de volgende noot ophalen. (ook wanneer het een note-off is!)

IF GetNote% THEN

' GetNote% is een funktie die wanneer er een volledige note-message binnenliep, True

' retourneert. In dat geval zijn automatisch de variabelen NuNoot%(0) en NuVelo%(0) bepaald.

' Wanneer RTSource% niet False is, retourneert GetNote% nootinformatie vanuit de

' alternatieve device-input.

IF NuNoot%(0) AND NuVelo%(0) > 0 THEN

ChordRequest% = True

Midi 144: Midi NuNoot%(0): Midi NuVelo%(0)

OldNoot%(0) = NuNoot%(0)

OldVelo%(0) = NuVelo%(0)

END IF

IF NuNoot%(0) AND NuVelo%(0) = 0 THEN

' rust:

IF OldNoot%(0) = NuNoot%(0) THEN

Midi 144: Midi NuNoot%(0): Midi 0

OldNoot%(0) = NuNoot%(0)

OldVelo%(0) = False

FOR icn% = 1 TO NrVoices%

IF OldNoot%(icn%) AND OldVelo%(icn%) THEN

Midi 144 + icn%: Midi OldNoot%(icn%): Midi 0

OldNoot%(icn%) = OldNoot%(icn%)

OldVelo%(icn%) = False

END IF

NEXT icn%

ChordRequest% = False :SolveTo% = 1

END IF

ChordRequest% = False

END IF

ELSE

ChordRequest% = False

NuNoot%(0) = False :NuVelo%(0) = False

END IF

END IF

IF ChordRequest% = True THEN

' ga na welk rangnummer de nu te harmonizeren

' noot heeft in de spektraalreeks Harmseries% (1 TO UBOUND(Ladder%))

HPointer% = HaalWijzer%(NuNoot%(0)) ' de parameter is die voor Stem%(0)

' indien nu HPointer% nog False is, dan behoort deze noot niet tot de tot

' nu toe geldende spektraalladder.In dit geval moeten we de grondtoon Tc%

' volgens de stappen van Harmseries% opschuiven tot de noot overeenkomt met noot

' in een nieuwe spektraalladder: SolveTo% will be set + aanpassing ladder% ,

' harmseries% en Tc%. De nieuwe toonladder staat reeds in LadMat%()

IF HPointer% = False THEN

' no matching note found in the scale... bekijk de tweede (enz...) noot van de huidige

' spektraalladder en ga na of de te harmoniseren noot behoort tot haar spektrum:

' Aangezien in Getparam1 werd nagegaan dat modulatie binnen het gegeven systeem in

' elk geval mogelijk is, hoeven we geen endless loops meer te vrezen.

SELECT CASE Rubberbanding%

CASE 0

DO ' tel van 0 tot NrTrans%:

TcCount% = (TcCount% + 1) MOD (NrTrans% + 1)

' de modulaties doorlopen nu ringvormig alle berekende mogelijkheden.

NewTc% = LadMat%(TcCount%, 1)

FOR ip% = 1 TO NrNotes%

HarmSeries%(ip%) = LadMat%(TcCount%, ip%)

NEXT ip%

HPointer% = HaalWijzer%(NuNoot%(0))

LOOP UNTIL HPointer%

CASE 1

Flag% = False

DO

' tel van NrTrans% tot 0, stapje per stapje:

IF TcCount% = 0 THEN Flag% = True

IF (TcCount% > 0) AND (Flag% = False) THEN

TcCount% = (TcCount% - 1) MOD (NrTrans% + 1)

IF TcCount% = 0 THEN Flag% = True

ELSE

TcCount% = (TcCount% + 1) MOD (NrTrans% + 1)

END IF

' de modulaties lopen nu stap per stap terug richting originele Tc%

NewTc% = LadMat%(TcCount%, 1)

FOR ip% = 1 TO NrNotes%

HarmSeries%(ip%) = LadMat%(TcCount%, ip%)

NEXT ip%

HPointer% = HaalWijzer%(NuNoot%(0))

LOOP UNTIL HPointer%

CASE 2

TcCount% = -1 ' reset TcCount% en probeer het met de oorspronkelijke toonladder

DO

' tel van 0 tot NrTrans%, stapje per stapje:

TcCount% = (TcCount% + 1) MOD (NrTrans% + 1)

' de modulaties lopen nu stap per stap telkens weer vanaf Tc%

NewTc% = LadMat%(TcCount%, 1)

FOR ip% = 1 TO NrNotes%

HarmSeries%(ip%) = LadMat%(TcCount%, ip%)

NEXT ip%

HPointer% = HaalWijzer%(NuNoot%(0))

LOOP UNTIL HPointer%

END SELECT

Tc% = NewTc% ' = LadMat%(TcCount%,1)

' verplicht het programma tot het schrijven van een akkoord bestaande uit

' de hoogste spektraalkomponenten van de nieuwste spektraalladder.

' de modulatie verloopt dus via een onvoorbereidde dissonant.

SolveTo% = NrNotes%

END IF ' endif voor HPointer%= False !

' Hier MOET HPointer% > 0 !!!!!!

IF SolveTo% THEN

' de variabele SolveTo% geeft de graad van het akkoord dat diende geschreven te worden op

' grond van het vorige akkoord, of op grond van de uitgevoerde modulatie.

IF Rest% THEN

GradeBotChord

SolveTo% = False

END IF

SELECT CASE SolveTo%

CASE 1

GradeBotChord

SolveTo% = False

CASE UBOUND(HarmSeries%)

GradeTopChord

SolveTo% = 1

CASE ELSE

' schrijft helemaal geen akkoord!

SolveTo% = False

END SELECT

ELSE

' geval waarin we de vrijheid hebben tot akkoordvorming:

' hierbij laten we het te vormen akkoord afhangen van de plaats van

' de nieuwe noot in de harmonische ladder. deze plaats vinden we terug in HPointer%

' In dit harmonisch systeem wordt geen rekening gehouden met de opvolger

' van de noot. Daardoor is het algoritme ook goed bruikbaar voor real-time toepassingen.

SELECT CASE HPointer%

CASE 0

STOP: ' dit mag niet voorkomen!

GradeSubChord

CASE IS = UBOUND(HarmSeries%) / 2

GradeCenChord

CASE IS > UBOUND(HarmSeries%) - NrVoices%

GradeTopChord

CASE IS < NrVoices%

GradeBotChord

CASE IS >= UBOUND(HarmSeries%) / 2

GradeAbochord

CASE IS <= UBOUND(HarmSeries%) / 2

GradeSubChord

CASE ELSE

GradeCenChord

END SELECT

SolveTo% = False ' reset de schrijfverplichtingspointer

END IF

' afwerking : ******************* file mode ****************************

IF RealTim% = False THEN

P%(i&, 0) = Tc%: ' save grondnootpointer

' leg nu de toegevoegde noten in de juiste tessituur:

' en schrijf meteen ook de dinamiek naar de betreffende stemmen

FOR icnt% = 1 TO NrVoices%

' write the notes to the P%() array:

P%(i&, Stem%(icnt%)) = NuNoot%(icnt%)

' dynamiek zoals in de opgegeven stem:

P%(i&, Stem%(icnt%) + 1) = NuVelo%(0)

DO

P%(i&, Stem%(icnt%)) = P%(i&, Stem%(icnt%)) + 12

LOOP UNTIL P%(i&, Stem%(icnt%)) >= Vlow%(icnt%)

' hierbij kunnen we de intervallen tegenover de vorige noot nog zo omdraaien dat de

' melodische intervallen nooit groter zijn dan een tritonus.

' cfr. de routines in Melodize In het realtime geval, gebeurt dit in de

' procedure RTBinder onthoudt de vorige noten:

VorigeNoot%(icnt%) = NuNoot%(icnt%)

NEXT icnt%

END IF

' ***************************************************************************

' ******************************* Debug section *****************************

IF Debug THEN

IF Debuglevel% = 10 THEN

TestChords

Kleur 14

IF RealTim% = False THEN

LOCATE 10, 5: PRINT "Count:",

PRINT USING "######"; i&;

ELSE

LOCATE 10, 5: PRINT "Time:",

PRINT TIME$;

END IF

FOR icnt% = 0 TO NrVoices%

LOCATE 11 + icnt%, 10:

PRINT USING "##"; Stem%(icnt%);

PRINT " ",

PRINT USING "###"; VorigeNoot%(icnt%);

PRINT " ",

PRINT USING "###"; NuNoot%(icnt%);

PRINT " ",

IF RealTim% = False THEN

PRINT USING "###"; VolgendeNoot%(icnt%);

END IF

NEXT icnt%

END IF

END IF

' ***************************************************************************

IF RealTim% THEN

' in realtime verzorgt de RTbinder procedure het geheugen en de melodische clean-up.

RTbinder

' procedure voor midiuit:

FOR icnt% = 1 TO NrVoices%

' onthoudt de vorige noten:

VorigeNoot%(icnt%) = NuNoot%(icnt%)

IF NuNoot%(icnt%) THEN

IF OldVelo%(icnt%) AND OldNoot%(icnt%) THEN

Midi 144 + icnt%: Midi OldNoot%(icnt%): Midi 0

OldVelo%(icnt%) = False:OldNoot%(icnt%) = False

END IF

Midi 144 + icnt%: Midi NuNoot%(icnt%): Midi NuVelo%(icnt%)

OldNoot%(icnt%) = NuNoot%(icnt%):OldVelo%(icnt%) = NuVelo%(icnt%)

END IF

NEXT icnt%

END IF

END IF ' endif voor te harmonizeren noot

' IF ChordRequest% ...

IF RealTim% = False THEN

i& = i& + 1 ' main loop counter for P()-array

IF i& > UBOUND(P%, 1) THEN EXIT DO

ELSE

i& = (i& + 1) AND &H7FFF

k$ = INKEY$: IF k$ = CHR$(27) THEN EXIT DO

LOCATE 50, 10: PRINT i&; " ";

'IF NuNoot%(0) THEN LOCATE 20, 10: PRINT NuNoot%(0); NuVelo%(0); " ";

END IF

LOOP

' clean up for files and playback of results:

IF RealTim% = False THEN

IF Merging = False THEN Melodize: ' gaat niet in het merge-geval!

Binder

Mplay

Writfil

END IF

CLS

END

SUB Binder

' Deze Binder werkt slechts op afgewerkte P() arrays! Hij kan niet in real-time worden gebruikt. Hij is daarvoor echter wel

' aan te passen... -> procedure RTbinder wanneer een noot gelijk is aan haar voorganger, sla ze dan niet

' opnieuw aan, maar bind ze met de vorige noot. merk op dat we (nog) geen rekening houden met de metriek!)

' Deze procedure werd reeds veralgemeend tot n-stemmen...

DIM NootBind%(0 TO NrVoices%): ' local logic array

FOR j% = 1 TO NrVoices%

i& = 0

DO

VorigeNoot%(j%) = P%(i&, Stem%(j%)):i& = i& + 1

LOOP UNTIL VorigeNoot%(j%) > 0

NEXT j%

i& = 0

DO

IF P%(i&, Stem%(0)) > 0 AND P%(i&, Stem%(0) + 1) > 0 THEN

FOR j% = 1 TO NrVoices%

NootBind%(j%) = False:

IF P%(i&, Stem%(j%)) MOD 12 = VorigeNoot%(j%) MOD 12 AND (i& > 0) THEN

P%(i&, Stem%(j%)) = False: P%(i&, Stem%(j%) + 1) = False

NootBind%(j%) = True:

END IF

IF NootBind%(j%) = False THEN VorigeNoot%(j%) = P%(i&, Stem%(j%))

NEXT j%

END IF

i& = i& + 1

LOOP UNTIL i& > UBOUND(P%, 1)

' aangezien het nu echter kan voorvallen dat note-offs gebonden werden moeten we alle note offs weer terug plaatsen zoals ze stonden:

i& = 0

DO

IF (P%(i&, Stem%(0)) > 0) AND (P%(i&, Stem%(0) + 1) = False) THEN

FOR j% = 1 TO NrVoices%

IF P%(i&, Stem%(j%)) = False THEN

ii& = i&

DO

VorigeNoot%(j%) = P%(ii&, Stem%(j%)): ii& = ii& - 1

LOOP UNTIL (VorigeNoot%(j%) > 0) OR (ii& = 0)

P%(i&, Stem%(j%)) = VorigeNoot%(j%): P%(i&, Stem%(j%) + 1) = False

END IF

NEXT j%

END IF

i& = i& + 1

LOOP UNTIL i& > UBOUND(P%, 1)

END SUB

SUB Blank1059

FOR iloc% = 10 TO 59: LOCATE iloc%, 1: PRINT SPACE$(79);: NEXT iloc%

Logo

END SUB

FUNCTION FreqNote% (F!)

' deze funktie retourneert de midi-noot die het dichtst een overgedragen frekwentie benaderd.

FreqNote% = 12 * (LOG(F!) - LOG(Gronddo!)) / (LOG(2))

END FUNCTION

SUB GetMatrix

' needs on input: NrHarms%, NrNotes%, NrTrans% Returns: LadMat%(),resizes: NrTrans%, composes a transpositionmatrix based on NrHarms%, NrNotes% NrTrans%

OldNrTrans% = NrTrans%

REDIM LadMat%(NrNotes% * (NrTrans% + 1), 1 TO NrNotes%)

REDIM OldHarmSer%(0 TO NrHarms%): ' ? to NrNotes% ???

' Save original scale

Maakladder Tc% ' first rebuild it (with shared NrHarms%)

FOR i% = 1 TO NrNotes%:OldHarmSer%(i%) = HarmSeries%(i%):NEXT i%

' stap 1:Vul LadMat met alle mogelijke transposities

MetaPt% = 1: NrTrans% = 0

FOR i% = 0 TO OldNrTrans%

FOR j% = 1 TO NrNotes%

Toon% = OldHarmSer%(j%): Maakladder Toon%

FOR Hptr% = 1 TO NrNotes%: LadMat%(NrTrans%, Hptr%) = HarmSeries%(Hptr%):NEXT Hptr%

NrTrans% = NrTrans% + 1

NEXT j%

IF i% + 2 <= UBOUND(OldHarmSer%) THEN

Maakladder OldHarmSer%(i% + 2): ' may overflow!!!!

ELSE

MetaPt% = MetaPt% + 1

IF MetaPt% > NrNotes% THEN MetaPt% = 2

Maakladder LadMat%(NrTrans% - 1, MetaPt%)

END IF

FOR im% = 1 TO NrNotes%: OldHarmSer%(im%) = HarmSeries%(im%): NEXT im%

NEXT i%

NrTrans% = NrTrans% - 1

' nu hebben we een rechthoekige matrix.Alleen wanneer OldNrTrans%=0 is de matrix vierkant Deze gaan we verder uitdunnen.

' stap 2: onderzoek nu LadMat%() en zie of alle mogelijke noten erin voorkomen. Zoniet kunnen we immers niet vrij moduleren! We definieren een tijdelijk array, waarin we voor elke van de 12 noten aanduiden of ze al dan niet in de matrix staan. We initializeren het zo dat alle noten bij het begin afwezig zijn.

DIM noot%(0 TO 11)

FOR im% = 0 TO 11

noot%(im%) = False

NEXT im%

' we lopen de hele matrix af, tot we alle noten hebben teruggevonden. Vanzodra dat het geval is

' weten we meteen wat de minimale grootte is voorde matrix en dus voor het aantal mogelijke modulaties.

NrTrans% = 0

DO

FOR jm% = 1 TO NrNotes%

FOR jim% = 0 TO 11

IF LadMat%(NrTrans%, jm%) = jim% THEN

noot%(jim%) = 1

END IF

NEXT jim%

NEXT jm%

' test nu voor de huidige stand van NrTrans% of we reeds alle noten hebben gehad:

tel% = 0

FOR jim% = 0 TO 11

tel% = tel% + noot%(jim%)

NEXT jim%

IF tel% = 12 THEN

EXIT DO

ELSE

NrTrans% = NrTrans% + 1

END IF

LOOP UNTIL tel% = 12

' De dimensionering van LadMat% is nu te groot.Om geheugen te sparen (vrij te maken) voeren we

' een kopieerroutine uit waarna de maten van LadMat% opnieuw optimaal zullen zijn

DIM Tmp%(0 TO NrTrans%, 1 TO NrNotes%)

FOR i% = 0 TO NrTrans%

FOR j% = 1 TO NrNotes%

SWAP Tmp%(i%, j%), LadMat%(i%, j%)

NEXT j%

NEXT i%

REDIM LadMat%(0 TO NrTrans%, 1 TO NrNotes%)

FOR i% = 0 TO NrTrans%

FOR j% = 1 TO NrNotes%

SWAP Tmp%(i%, j%), LadMat%(i%, j%)

NEXT j%

NEXT i%

ERASE Tmp%

' LadMat%(0 TO NrTrans%, 1 TO NrNotes%) is nu onze matrix die we volledig tonen aan de gebruiker:

ShowMatrix

END SUB

FUNCTION GetNote%

STATIC noot%, nt%, status%, velo%, vl%, FifoReadPtr%

SELECT CASE RTSource%

CASE False

' this function should return a note from the midibuffer Fifo% (the most recent note

' played, since it is monophonic) taking into regard only notes flowing in on

' the channel indicated (MidiInChannel%)

IF FifoReadPtr% = FifoWritePtr% THEN GetNote% = False: EXIT FUNCTION

DO UNTIL FifoReadPtr% = FifoWritePtr%

SELECT CASE Fifo%(FifoReadPtr%)

CASE IS >= 240

GOTO IncPtr

CASE IS > 144 + MidiInChannel%

' throw away... but reset variables...

nt% = 0: vl% = 0: status% = 0

GOTO IncPtr

CASE (144 + MidiInChannel%), (128 + MidiInChannel%)

status% = Fifo%(FifoReadPtr%)

nt% = False: ' reset note en velo logic

vl% = False

GOTO IncPtr

CASE IS < 128

IF status% = 144 + MidiInChannel% THEN

noot% = Fifo%(FifoReadPtr%)

nt% = True: vl% = False: status% = False

GOTO IncPtr

END IF

IF (status% = False) AND (nt% = True) THEN

velo% = Fifo%(FifoReadPtr%)

nt% = False: vl% = True: status% = True

GOTO IncPtr

END IF

IF status% = True THEN

' support for running status...

noot% = Fifo%(FifoReadPtr%)

nt% = True: vl% = False: status% = False

GOTO IncPtr

END IF

END SELECT

IncPtr:

FifoReadPtr% = (FifoReadPtr% + 1) AND MidiBuffer

LOOP

IF vl% = True THEN

NuNoot%(0) = noot%: ' these are shared!

NuVelo%(0) = velo%

GetNote% = True

ELSE

NuNoot%(0) = False

NuVelo%(0) = False

GetNote% = False

END IF

CASE 1

' egg input

' NuNoot%(0)= ADC%(...)

' NuVelo%(0) = ....

' GetNote%= True ... of ... False

GetNote% = False

CASE 2

' BOM input

GetNote% = False

CASE 3

' DIANA input

GetNote% = False

END SELECT

END FUNCTION

SUB Getparam1

' returns the real-time variable RealTim% as True or False. returns a value for the real-time input device in RTSource%

' returns the number of voices to be added in counterpoint: NrVoices% returns a number for Stem%(0), Stem%(1), Stem%(2), Stem%(3) ...

' Stem%(0) is always the voice number to be harmonized

ScrLine% = 10

RealTim% = False

Merging% = False

RTSource% = -1

Blank1059

Kleur 2 :LOCATE ScrLine%, 10: ScrLine% = ScrLine% + 1

PRINT "This program has two modes of operation:";

LOCATE ScrLine%, 15: ScrLine% = ScrLine% + 1

PRINT "1- It can work on an existing P%(i,j) input file";

LOCATE ScrLine%, 15: ScrLine% = ScrLine% + 1

PRINT "2- It can work on real-time device input";

LOCATE ScrLine%, 10: ScrLine% = ScrLine% + 1

PRINT "Choose an operational mode (1-2) [Default=1]";

Kleur 13 :RealTim% = -1

DO

k$ = INKEY$

IF k$ <> "" THEN RealTim% = VAL(k$)

LOOP UNTIL k$ <> "" AND (RealTim% > -1 AND RealTim% < 3)

IF RealTim% = 0 THEN RealTim% = False

IF RealTim% = 1 THEN RealTim% = False

IF RealTim% = 2 THEN RealTim% = True

IF RealTim% THEN

LOCATE ScrLine%, 10: Kleur 14: ScrLine% = ScrLine% + 1

PRINT "Choose input device:";

LOCATE ScrLine%, 15: ScrLine% = ScrLine% + 1

PRINT "0- Midi input";

LOCATE ScrLine%, 15: ScrLine% = ScrLine% + 1

PRINT "1- <EGG> device ";

LOCATE ScrLine%, 15: ScrLine% = ScrLine% + 1

PRINT "2- <Anacomp> device ";

LOCATE ScrLine%, 15: ScrLine% = ScrLine% + 1

PRINT "3- <Diana> device ";

LOCATE ScrLine%, 20: ScrLine% = ScrLine% + 1: Kleur 13

PRINT "Choice ?";

DO :k$ = INKEY$: LOOP UNTIL k$ <> ""

PRINT k$;

IF VAL(k$) > 0 AND VAL(k$) < 4 THEN

RTSource% = VAL(k$)

ELSE

RTSource% = 0: ' default= midi-input

END IF

END IF

SELECT CASE RealTim%

CASE IS = False

Rbin

DO

Blank1059: LOCATE 10, 5: Kleur 14

PRINT "Give number of the voice to be harmonized ?";

LOCATE 11, 5: Kleur 6: PRINT "(Select 1 to 7)";

LOCATE 11, 30: Kleur 13 : PRINT "Choice ?";

DO

k$ = INKEY$

LOOP UNTIL VAL(k$) > 0 AND VAL(k$) < 8

Pv% = VAL(k$)

IF Pv% = 0 THEN Pv% = 1: ' sets default

Pv% = (Pv% * 2) - 1: ' mapping on 1,3,5,7,9,11,13

LOOP UNTIL (Pv% MOD 2 = 1) AND (Pv% > 0) AND (Pv% < 14)

LOCATE 12, 5: Kleur 14

PRINT "Merge new chords with existing notes? (Y/N)";

DO

k$ = UCASE$(INKEY$)

LOOP UNTIL k$ <> ""

IF k$ = "Y" THEN Merging% = True ELSE Merging% = False

CASE IS = True

SELECT CASE RTSource%

CASE 0

' geval midi-input

Blank1059: LOCATE 10, 5: Kleur 14

PRINT "Give midi-channel to use as voice to be harmonized ?";

LOCATE 11, 5: Kleur 6: PRINT "(Select 0 to F [Hex])";

LOCATE 11, 30: Kleur 13: PRINT "Choice ?";

MidiInChannel% = -1

DO

k$ = UCASE$(INKEY$)

IF k$ <> "" THEN

k$ = "&H" + k$ ' nice programmers trick

MidiInChannel% = VAL(k$)

END IF

LOOP UNTIL MidiInChannel% >= 0 AND MidiInChannel% < 16

Pv% = 1: ' sets dummy

PRINT MidiInChannel%;

CASE 1

' geval <EGG> controller with contec-board

' to be implemented. Needs appropriate hardware...

Pv% = 1

CASE 2

' geval anacomp with Arcom board

Pv% = 1

CASE 3

' geval diana with contec board

Pv% = 1

END SELECT

END SELECT

LOCATE 13, 5: Kleur 14

PRINT "How many voices do you want to add (1-6)";

Kleur 13: PRINT "[3]?";

NrVoices% = -1

DO

k$ = INKEY$

IF k$ <> "" THEN NrVoices% = VAL(k$)

LOOP UNTIL NrVoices% > -1 AND NrVoices% < 7

IF NrVoices% = 0 THEN NrVoices% = 3 ' default= 4 voices

PRINT NrVoices%; "Voices chosen";

' Redimension arrays:

REDIM Stem%(0 TO NrVoices%)

REDIM VorigeNoot%(0 TO NrVoices%)

REDIM VorigeVelo%(0 TO NrVoices%)

REDIM NuNoot%(0 TO NrVoices%)

REDIM NuVelo%(0 TO NrVoices%)

REDIM VolgendeNoot%(0 TO NrVoices%)

REDIM Vlow%(0 TO NrVoices%)

REDIM Vhigh%(0 TO NrVoices%)

' Set voice to be harmonized to Pv%

Stem%(0) = Pv%

Valid% = False

DO

pl% = 15 ' schermregelcounter

FOR j% = 1 TO NrVoices%

LOCATE pl%, 5: pl% = pl% + 1: Kleur 14

PRINT "Give array-voice pointer of voice"; j%; " to be added...";

LOCATE pl%, 5: pl% = pl% + 1: Kleur 6

PRINT "(Select 1,2,3,4,5,6,7 but not ";

Kleur 9

IF j% > 1 THEN

FOR jj% = j% - 1 TO 0 STEP -1:PRINT (Stem%(jj%) \ 2) + 1; :NEXT jj%

Kleur 6: PRINT ")";

ELSE

PRINT (Stem%(0) \ 2) + 1;:Kleur 6: PRINT ")";

END IF

LOCATE pl%, 5: pl% = pl% + 1: Kleur 13

INPUT "Choice?"; kk%

Stem%(j%) = (kk% * 2) - 1

IF Stem%(j%) <= False THEN Stem%(j%) = (j% * 2) + 1:

' sets default 1,3,5,7,9...

LOCATE pl% - 1, 40

Kleur 12: PRINT "ptr="; (Stem%(j%) \ 2) + 1;

LOCATE pl%, 10: PRINT " "; : pl% = pl% + 1

NEXT j%

' fail-safe test op validiteit:

FOR jj% = 0 TO NrVoices%

FOR jjj% = jj% + 1 TO NrVoices%

IF Stem%(jj%) = Stem%(jjj%) THEN Valid% = True

NEXT jjj%

IF Stem%(jj%) MOD 2 = False THEN Valid% = True

NEXT jj%

LOOP UNTIL Valid% = False

END SUB

SUB GetToneSystem

' returns tonal center Tc% at start return a value for the Rubberbanding% variable, which determines the way modulations are handled.

' returns number of harmonics to build scale from checks working conditions: minimal number of notes in scale must be larger than NrVoices%

Blank1059:Tc% = -1:pl% = 10

LOCATE pl%, 10: Kleur 14: pl% = pl% + 1

PRINT "Give tonal center to start from? (0-127) ";

LOCATE pl%, 10: Kleur 13

DO

INPUT "Choice? "; Tc%

Tc% = Tc% MOD 12

LOOP UNTIL Tc% > -1 AND Tc% < 12

pl% = pl% + 2: ' get modulation style options:

LOCATE pl%, 10: Kleur 14: pl% = pl% + 1

PRINT "0- No Rubberbanding : Free modulation from "; Tc%;

LOCATE pl%, 10: Kleur 14: pl% = pl% + 1

PRINT "1- Gentle Rubberbanding: try to get back to tonal center";

LOCATE pl%, 10: Kleur 14: pl% = pl% + 1

PRINT "2- Severe Rubberbanding: always harmonize close to tonal center";

LOCATE pl%, 20: Kleur 13

PRINT "Choice ?";

DO: k$ = INKEY$: LOOP UNTIL k$ <> ""

PRINT k$;

pl% = pl% + 1

SELECT CASE VAL(k$)

CASE 0

Rubberbanding% = False

CASE 1

Rubberbanding% = 1

CASE 2

Rubberbanding% = 2

CASE ELSE

Rubberbanding% = False

END SELECT

' calculate minimum number of harmonics condition gebruik makend van de betreffende funktie

pl% = pl% + 1

MinNrHarms% = HarmSize%

' let the user adapt the number of harmonics if he thinks the minimum value is too small

LOCATE pl%, 10: Kleur 14: pl% = pl% + 1

PRINT "The minimum number of harmonics is ="; MinNrHarms%;

LOCATE pl%, 10: Kleur 14: pl% = pl% + 1

PRINT "Is this enough?";

Kleur 13: PRINT "(Y/N)? ";

DO: k$ = UCASE$(INKEY$): LOOP UNTIL k$ <> ""

PRINT k$;

IF k$ = "N" THEN

DO

LOCATE pl%, 10: Kleur 14

PRINT "How many harmonics do you want to use ?";

Kleur 13: INPUT k%

pl% = pl% + 1

LOOP UNTIL (k% > MinNrHarms%) AND (k% < 28)

NrHarms% = k%

ELSE

NrHarms% = MinNrHarms%

END IF

Maakladder Tc% ' Tc% moet gebleven zijn zoals opgegeven.

END SUB

SUB GetVoicing

' returns compass for Stem%(n): Vhigh%(n), Vlow%(n) get parameters for voicing loop: defaults per stem in kwarten naar omlaag - tessituur: oktaaf + kwart = 17. zou moeten afhankelijk zijn van de keuze van Stem%(0), en meer nog,van de tessituurligging van de betreffende stem in de gegeven BIN file! => procedure schrijven die deze ligging automatisch kan bepalen? Het probleem is dat dit real-time gebruik zou uitsluiten...

Blank1059: pl% = 10

FOR i% = 1 TO NrVoices%

DO

LOCATE pl%, 5: pl% = pl% + 1: Kleur 14

PRINT "Give lowest midi-note for voice nr."; i%; "="; Stem%(i%); " (24-73) ";

LOCATE pl%, 5: pl% = pl% + 1: Kleur 13

INPUT "Choice? "; Vlow%(i%)

' default setting code:

IF Vlow%(i%) = 0 THEN

SELECT CASE Stem%(i%)

CASE 1

SELECT CASE NrVoices%

CASE 1

Vlow%(i%) = 62

CASE 2

Vlow%(i%) = 64

CASE 3

Vlow%(i%) = 66

CASE 4

Vlow%(i%) = 68

CASE 5

Vlow%(i%) = 70

CASE 6

Vlow%(i%) = 74

CASE ELSE

STOP

END SELECT

CASE 3

SELECT CASE NrVoices%

CASE 1

Vlow%(i%) = 48

CASE 2

Vlow%(i%) = 52

CASE 3

Vlow%(i%) = 55

CASE 4

Vlow%(i%) = 58

CASE 5

Vlow%(i%) = 63

CASE 6

Vlow%(i%) = 67

CASE ELSE

STOP

END SELECT

CASE 5

SELECT CASE NrVoices%

CASE 1

Vlow%(i%) = 48

CASE 2

Vlow%(i%) = 52

CASE 3

Vlow%(i%) = 54

CASE 4

Vlow%(i%) = 55

CASE 5

Vlow%(i%) = 56

CASE 6

Vlow%(i%) = 57

CASE ELSE

STOP

END SELECT

CASE 7

SELECT CASE NrVoices%

CASE 1

Vlow%(i%) = 48

CASE 2

Vlow%(i%) = 47

CASE 3

Vlow%(i%) = 40

CASE 4

Vlow%(i%) = 42

CASE 5

Vlow%(i%) = 43

CASE 6

Vlow%(i%) = 46

CASE ELSE

STOP

END SELECT

CASE 9

SELECT CASE NrVoices%

CASE 1

Vlow%(i%) = 48

CASE 2

Vlow%(i%) = 47

CASE 3

Vlow%(i%) = 46

CASE 4

Vlow%(i%) = 44

CASE 5

Vlow%(i%) = 42

CASE 6

Vlow%(i%) = 46

CASE ELSE

STOP

END SELECT

CASE 11

SELECT CASE NrVoices%

CASE 1

Vlow%(i%) = 48

CASE 2

Vlow%(i%) = 46

CASE 3

Vlow%(i%) = 44

CASE 4

Vlow%(i%) = 42

CASE 5

Vlow%(i%) = 40

CASE 6

Vlow%(i%) = 38

CASE ELSE

STOP

END SELECT

CASE 13

SELECT CASE NrVoices%

CASE 1

Vlow%(i%) = 46

CASE 2

Vlow%(i%) = 44

CASE 3

Vlow%(i%) = 42

CASE 4

Vlow%(i%) = 40

CASE 5

Vlow%(i%) = 38

CASE 6

Vlow%(i%) = 36

CASE ELSE

STOP

END SELECT

CASE ELSE

STOP: ' kan niet!

END SELECT

END IF

LOOP UNTIL Vlow%(i%) > 23 AND Vlow%(i%) < 73

LOCATE pl% - 1, 40:

Kleur 10: PRINT " low= "; Vlow%(i%);

DO

LOCATE pl%, 5: pl% = pl% + 1: Kleur 14

PRINT "Give highest midi-note for voice "; i%; "="; Stem%(i%); " ("; Vlow%(i%) + 12; "-110)";

LOCATE pl%, 5: pl% = pl% + 1: Kleur 13

INPUT "Choice? "; Vhigh%(i%)

IF Vhigh%(i%) = 0 THEN Vhigh%(i%) = Vlow%(i%) + 17: ' default

LOCATE pl% - 1, 40

Kleur 10: PRINT " high="; Vhigh%(i%);

LOCATE pl%, 10: PRINT " "; : pl% = pl% + 1

LOOP UNTIL Vhigh%(i%) > Vlow%(i%) + 11 AND Vhigh%(i%) < 111

NEXT i%

END SUB

SUB GradeAbochord

' schrijf een akkoord gelegen vlak boven de gegeven spektraalnoot

iloc% = 1 ' stemmenteller

cnt% = 1

DO

NuNoot%(iloc%) = HarmSeries%(HPointer% + cnt%)

IF iloc% = NrVoices% THEN EXIT DO

cnt% = cnt% + 1 : iloc% = iloc% + 1

LOOP UNTIL HPointer% + cnt% > UBOUND(HarmSeries%)

IF iloc% >= NrVoices% THEN EXIT SUB

' wanneer nog niet alle stemmen gevuld zijn nu, gaan we verder naar omlaag in de reeks

cnt% = 1

DO

NuNoot%(iloc%) = HarmSeries%(HPointer% - cnt%)

cnt% = cnt% - 1

IF iloc% = NrVoices% THEN EXIT DO

iloc% = iloc% + 1

LOOP UNTIL HPointer% - cnt% < 1

END SUB

SUB GradeBotChord

' This procedure writes a chord on the first NrVoices% notes of the harmonic series (1-2-3-4-> NrVoices%)

' memo: P%(i&, Stem%(0))= Harmseries%(HPointer%) of: NuNoot%(0)= HarmSeries%(HPointer%).generalized to accomodate n-part counterpoint.

iloc% = 1:cnt% = 1

DO

IF cnt% <> HPointer% THEN

NuNoot%(iloc%) = HarmSeries%(cnt%): iloc% = iloc% + 1

END IF

cnt% = cnt% + 1

LOOP UNTIL iloc% > NrVoices%

END SUB

SUB GradeCenChord

' writes the chord containing all harmonics as close as possible below and above, but adjacent to the given note

IF HPointer% < (NrVoices% / 2) THEN

GradeBotChord

EXIT SUB

END IF

IF HPointer% > (UBOUND(HarmSeries%) - (NrVoices% / 2)) THEN GradeTopChord:EXIT SUB

' Slechts in de nu nog overblijvende gevallen is het mogelijk het akkoord rond de gegeven

' noot op te bouwen.

loccnt% = 0

FOR iloc% = (NrVoices% / 2) TO 1 STEP -1

IF HPointer% - iloc% >= LBOUND(HarmSeries%) THEN

NuNoot%(iloc%) = HarmSeries%(HPointer% - iloc%):loccnt% = loccnt% + 1

END IF

NEXT iloc%

lnc% = 0

FOR iloc% = (NrVoices% / 2) TO NrVoices%

lnc% = lnc% + 1

IF HPointer% + lnc% <= UBOUND(HarmSeries%) THEN

NuNoot%(iloc%) = HarmSeries%(HPointer% + lnc%):loccnt% = loccnt% + 1

END IF

NEXT iloc%

' ingeval NrVoices% oneven is kan het voorvallen dat we door het resultaat

' van de integer-deling, een stem tekort hebben in het akkoord.Vandaar volgende korrektie:

IF loccnt% < NrVoices% THEN NuNoot%(NrVoices%) = HarmSeries%(HPointer% + iloc%)

END SUB

SUB GradeSubChord

' writes the chord containing all harmonics below and adjacent to the given note

SELECT CASE HPointer%

CASE IS > NrVoices%

FOR iloc% = NrVoices% TO 1 STEP -1

NuNoot%(iloc%) = HarmSeries%(HPointer% - iloc%)

NEXT iloc%

CASE ELSE

' in this case the chord can not be written!thus we correct and write a fundamental chord instead:

GradeBotChord

END SELECT

END SUB

SUB GradeTopChord

' This procedure writes a chord composed of the highest spectral notes

' belonging to the series in consideration

SELECT CASE HPointer%

CASE IS = UBOUND(HarmSeries%)

FOR iloc% = 1 TO NrVoices%

NuNoot%(iloc%) = HarmSeries%(HPointer% - iloc%)

NEXT iloc%

CASE ELSE

iloc% = NrVoices% :cnt% = 0

DO

NuNoot%(iloc%) = HarmSeries%(UBOUND(HarmSeries%) - cnt%)

IF iloc% = 1 THEN EXIT DO

cnt% = cnt% + 1 : iloc% = iloc% - 1

LOOP UNTIL UBOUND(HarmSeries%) - cnt% = HPointer%

IF iloc% < 1 THEN EXIT SUB

' schrikkel de gegeven noot over

cnt% = cnt% + 1

DO

IF UBOUND(HarmSeries%) - cnt% >= 1 THEN

NuNoot%(iloc%) = HarmSeries%(UBOUND(HarmSeries%) - cnt%)

cnt% = cnt% + 1

IF iloc% = 1 THEN EXIT DO

iloc% = iloc% - 1

ELSE

EXIT DO

END IF

LOOP UNTIL iloc% < 1

END SELECT

END SUB

FUNCTION HaalWijzer% (k%)

' deze funktie retourneert de plaats van de opgegeven stem in de harmonische reeks

' HarmSeries%() Wanneer deze funktie 0 retourneert, dan werd de opgegeven noot niet gevonden

' in de harmonische reeks en is ze als laddervreemd te beschouwen.

' k% moet de overgedragen noot zijn!

hp% = False: n% = 1

DO

IF k% MOD 12 = HarmSeries%(n%) THEN

hp% = n%

EXIT DO

ELSE

n% = n% + 1

END IF

IF n% > NrNotes% THEN EXIT DO

LOOP UNTIL hp%

HaalWijzer% = hp%

END FUNCTION

FUNCTION HarmSize% STATIC

' this function should return the minimum amount of harmonics a scale should be calculated on, in order

' to make all 12 imaginable modulations possible.nodige input variabelen: Tc%, NrVoices% (must be SHARED)

' save oldTc on init:

DIM GoodNotes%(1 TO 12) ' local!

OldTc% = Tc%

NrHarms% = (NrVoices% * 2): ' value to try on init.

ig% = 0

DO

NrTrans% = 0

DO

FOR i% = 1 TO 12: GoodNotes%(i%) = True: NEXT i%

Maakladder Tc%

REDIM OldHarmSer%(1 TO NrNotes%)

FOR i% = 1 TO NrNotes%: OldHarmSer%(i%) = HarmSeries%(i%): NEXT i%

FOR Ton% = OldHarmSer%(1) TO OldHarmSer%(NrNotes%)

NrTrans% = NrTrans% + 1

Maakladder Ton%

FOR testnoot% = 0 TO 11

' ga na of testnoot% voorkomt in de nu geldende toonladder:

HPointer% = HaalWijzer%(testnoot%)

IF HPointer% > 0 THEN

IF ig% > 0 THEN

nt% = True

FOR j% = 1 TO ig%

IF testnoot% = GoodNotes%(j%) THEN nt% = False

NEXT j%

IF nt% = True THEN

ig% = ig% + 1: GoodNotes%(ig%) = testnoot%

END IF

ELSE

ig% = ig% + 1: GoodNotes%(ig%) = testnoot%

END IF

END IF

NEXT testnoot%

NEXT Ton%

IF ig% = 12 THEN

GOTO Uitgang1

'EXIT DO

ELSE

ig% = 0: NrTrans% = 0 :NrHarms% = NrHarms% + 1

END IF

LOOP

Uitgang1:

IF NrNotes% > NrVoices% + 1 THEN

IF ig% = 12 THEN EXIT DO

ELSE

ig% = 0

NrTrans% = 0 : NrHarms% = NrHarms% + 1

END IF

LOOP

IF ig% < 12 THEN STOP

ERASE GoodNotes% ' clear memory on exit.

HarmSize% = NrHarms% :Tc% = OldTc%

Maakladder Tc%

END FUNCTION

SUB Kleur (k%)

IF KleurScherm THEN COLOR k%

END SUB

SUB Logo

' teken kader:

Kleur 1

t$ = CHR$(176)

LOCATE 1, 1: PRINT STRING$(80, t$);

LOCATE 5, 1: PRINT STRING$(80, t$);

LOCATE 2, 1: PRINT t$;

LOCATE 3, 1: PRINT t$;

LOCATE 4, 1: PRINT t$;

LOCATE 2, 80: PRINT t$;

LOCATE 3, 80: PRINT t$;

LOCATE 4, 80: PRINT t$;

' schrijf de tekst gecentreerd:

Kleur 13

ln% = LEN(Tit1$): Plaats% = (80 - ln%) / 2: Kleur 9

LOCATE 2, Plaats%: PRINT Tit1$;

ln% = LEN(Tit2$): Plaats% = (80 - ln%) / 2: Kleur 9

LOCATE 3, Plaats%: PRINT Tit2$;

ln% = LEN(Tit3$): Plaats% = (80 - ln%) / 2: Kleur 9

LOCATE 4, Plaats%: PRINT Tit3$;

IF RealTim% THEN

ln% = LEN(Tit4$): Plaats% = (80 - ln%) / 2: Kleur 14

LOCATE 59, Plaats%: PRINT Tit4$;

END IF

Kleur 2

ln% = LEN(Ver$): Plaats% = (80 - ln%) / 2: Kleur 9

LOCATE 60, Plaats%: PRINT Ver$;

Kleur 7

END SUB

SUB Maakladder (GrondToon%)

' returns notescales based on a given Tc% dimensions HarmSeries%() and Ladder%()

' returns NrNotes% : number of notes in scale

REDIM HarmSeries%(1 TO NrHarms%)

F0! = NoteFreq!(GrondToon%):

n% = 0

FOR i% = 1 TO NrHarms%

noot% = FreqNote%(F0! * i%) MOD 12 :skip% = False

IF i% > 1 THEN

' vergelijk dan of we die noot niet reeds hadden...

FOR j% = n% TO 1 STEP -1

IF noot% = HarmSeries%(j%) THEN skip% = True

NEXT j%

END IF

IF skip% = False THEN

n% = n% + 1: HarmSeries%(n%) = noot%

END IF

NEXT i%

NrNotes% = n%

' nu hebben we een harmonischen reeks in harmoniekvolgorde bestaande uit n% verschillende noten.

REDIM Ladder%(1 TO NrNotes%)

' kopieer nu deze reeks naar het ladderarray:

FOR i% = 1 TO NrNotes%: Ladder%(i%) = HarmSeries%(i%):NEXT i%

' pas nu opnieuw de maten van Harmseries%() aan:

REDIM HarmSeries%(1 TO NrNotes%)

' en kopieer de reeks opnieuw:

FOR i% = 1 TO NrNotes%:HarmSeries%(i%) = Ladder%(i%): NEXT i%

' sorteer nu ladder%() in opklimmende volgorde:

FOR i% = 1 TO NrNotes%

FOR j% = i% + 1 TO NrNotes%

IF Ladder%(j%) < Ladder%(i%) THEN SWAP Ladder%(i%), Ladder%(j%)

NEXT j%

NEXT i%

END SUB

SUB Melodize

' Deze procedure werkt op een integraal en afgwerkt P%() array

' bereken de centrale noot van de tessituur voor elke toe te voegen stem:

DIM Cnote%(0 TO NrVoices%) ' We use a local static array

DIM Melo%(0 TO NrVoices%)

FOR i% = 1 TO NrVoices%

Cnote%(i%) = (Vlow%(i%) + Vhigh%(i%)) / 2

NEXT i%

' Zoek de eerste noot in de partituur

i& = 0

DO

NextNote% = P%(i&, Stem%(0))

i& = i& + 1

IF i& > UBOUND(P%, 1) THEN EXIT DO

LOOP UNTIL NextNote% > 0

FOR j% = 1 TO NrVoices%:VorigeNoot%(j%) = P%(i& - 1, Stem%(j%)):NEXT j%

' leg nu de melodielijnen zo dat alle sprongen rond Cnote%(1) resp. Cnote%(2) ,Cnote%(3) draaien

i& = 0

DO

IF P%(i&, Stem%(0)) THEN

FOR j% = 1 TO UBOUND(P%, 2) STEP 2

FOR iml% = 1 TO NrVoices%

IF j% = Stem%(iml%) THEN

IF P%(i&, Stem%(iml%)) - Cnote%(iml%) > 6 THEN

DO

P%(i&, Stem%(iml%)) = P%(i&, Stem%(iml%)) - 12

LOOP UNTIL P%(i&, Stem%(iml%)) - Cnote%(iml%) <= 6

END IF

IF P%(i&, Stem%(iml%)) - Cnote%(iml%) < -6 THEN

DO

P%(i&, Stem%(iml%)) = P%(i&, Stem%(iml%)) + 12

LOOP UNTIL P%(i&, Stem%(iml%)) - Cnote%(iml%) >= -6

END IF

END IF

NEXT iml%

NEXT j%

' **************************************************************************

' Als een noot gelijk is aan haar voorganger binnen dezelfde stem, leg ze in hetzelfde oktaaf en set

' de logische variabele: aan deze stem mag niet meer geraakt worden in verdere melodizeringsprocedures.

' **************************************************************************

FOR iml% = 1 TO NrVoices%

IF VorigeNoot%(iml%) MOD 12 = P%(i&, Stem%(iml%)) MOD 12 THEN

IF VorigeNoot%(iml%) THEN P%(i&, Stem%(iml%)) = VorigeNoot%(iml%)

Melo%(iml%) = True

ELSE

Melo%(iml%) = False

END IF

NEXT iml%

' **************************************************************************

' Wanneer een noot gelijk is aan een noot die voorkomt in een andere stem, wissel dan de noten om, zodat we

' een noot in die stem kunnen laten liggen.

' **************************************************************************

FOR iml% = 1 TO NrVoices% - 1

FOR jml% = iml% + 1 TO NrVoices%

IF Melo%(iml%) + Melo%(jml%) = 0 THEN

IF VorigeNoot%(iml%) MOD 12 = P%(i&, Stem%(jml%)) MOD 12 THEN

SWAP P%(i&, Stem%(iml%)), P%(i&, Stem%(jml%))

P%(i&, Stem%(iml%)) = VorigeNoot%(iml%)

P%(i&, Stem%(jml%)) = ((VorigeNoot%(jml%) \ 12) * 12) + (P%(i&, Stem%(jml%)) MOD 12)

Melo%(iml%) = True

END IF

END IF

IF Melo%(iml%) + Melo%(jml%) = 0 THEN

IF VorigeNoot%(jml%) MOD 12 = P%(i&, Stem%(iml%)) MOD 12 THEN

SWAP P%(i&, Stem%(jml%)), P%(i&, Stem%(iml%))

P%(i&, Stem%(jml%)) = VorigeNoot%(jml%)

P%(i&, Stem%(iml%)) = ((VorigeNoot%(iml%) \ 12) * 12) + (P%(i&, Stem%(iml%)) MOD 12)

Melo%(jml%) = True

END IF

END IF

NEXT jml%

NEXT iml%

' ************************************************************************

' ga nu na of door omwisseling van noten tussen de stemmen, de intervallen niet kunnen worden geminimaliseerd

' We onderzoeken eerst sekonden, en verder grotere intervallen om zodoende een meer melodische stemvoering te verkrijgen.

' ************************************************************************

FOR Interval% = 2 TO 12

FOR iml% = 1 TO NrVoices - 1

FOR jml% = iml% + 1 TO NrVoices%

IF Melo%(iml%) = 0 THEN

IF Melo%(jml%) = 0 THEN

IF ABS((VorigeNoot%(iml%) MOD 12) - (P%(i&, Stem%(jml%)) MOD 12)) < Interval% THEN

SWAP P%(i&, Stem%(iml%)), P%(i&, Stem%(jml%))

P%(i&, Stem%(iml%)) = ((VorigeNoot%(iml%) \ 12) * 12) + (P%(i&, Stem%(iml%)) MOD 12)

P%(i&, Stem%(jml%)) = ((VorigeNoot%(jml%) \ 12) * 12) + (P%(i&, Stem%(jml%)) MOD 12)

Melo%(iml%) = -1

END IF

END IF

END IF

NEXT jml%

NEXT iml%

NEXT Interval%

' ***************************************************************************

FOR iml% = 1 TO NrVoices%:VorigeNoot%(iml%) = P%(i&, Stem%(iml%)):NEXT iml%

END IF

i& = i& + 1

LOOP UNTIL i& > UBOUND(P%, 1)

ERASE Melo% ' clean up memory for reuse.

ERASE Cnote%

END SUB

SUB Midi (byte%)

' midi I/O routine. ' de counter FifoWritePtr% (SHARED!)wordt hier vooruitgeschoven.

' Deze counter is dus steeds >= FifoReadPtr% in de funktieprocedure GetNote%.

' Voor het uitsturen van midi-info wordt geen buffer gebruikt.

IF INP(Sp) < 128 THEN

DO

dummy% = INP(Dp)

IF dummy% < 240 THEN

Fifo%(FifoWritePtr%) = dummy%

FifoWritePtr% = (FifoWritePtr% + 1) AND MidiBuffer

END IF

LOOP UNTIL INP(Sp) AND 128

END IF

IF byte% > True THEN WAIT Sp, 64, 64: OUT Dp, byte%

END SUB

SUB Mplay

DIM no%(16):DIM nv%(16):Blank1059:Logo

DO: k$ = INKEY$: LOOP UNTIL k$ = ""

LOCATE 10, 10: Kleur 14

PRINT "Do you want to hear this spectral counterpoint? (Y/N) ";

DO :k$ = UCASE$(INKEY$):LOOP UNTIL k$ <> ""

IF k$ = "Y" THEN

LOCATE 12, 10: Kleur 14:PRINT "Tempo (MM Metronome number) ?:";

Kleur 13: INPUT MM

Tmp# = 60 / (MM * 8): ' de tweeendertigste noot is de eenheid in het array

FOR i& = 0 TO UBOUND(P%, 1)

T0# = TIMER

FOR ij% = 1 TO NrVoices%: 'UBOUND(P%, 2) STEP 2

j% = (ij% * 2) - 1

IF P%(i&, j%) > 0 THEN

IF no%(j%) AND (nv%(j%) > 0) THEN

Uit (143 + ij%):Uit no%(j%):Uit (0):no%(j%) = False:nv%(j%) = False

END IF

Uit (143 + ij%):Uit (P%(i&, j%)):Uit (P%(i&, j% + 1))

no%(j%) = P%(i&, j%): nv%(j%) = P%(i&, j% + 1)

END IF

NEXT ij%

DO UNTIL TIMER - T0# >= Tmp#: LOOP

NEXT i&

END IF

SLEEP 1

DO: LOOP UNTIL INKEY$ = ""

LOCATE 24, 10: Kleur 14:PRINT " Push any key to continue ... ";

DO: LOOP UNTIL INKEY$ <> ""

Blank1059

' schakel alle noten uit:

FOR j% = 1 TO NrVoices%:Uit (143 + j%): Uit (no%((j% * 2) - 1)): Uit 0:NEXT j%

END SUB

SUB Mpuuart

INITUART:

IF INP(&H331) AND 128 THEN

WAIT &H331, 64, 64:OUT &H331, &H3F: ' = 0011 1111 (63) =UART-command

ELSE

WHILE INP(&H331) < 128: dummy = INP(&H330):WEND: GOTO INITUART

END IF

END SUB

FUNCTION NoteFreq! (byte%)

' deze funktie retourneert de frekwentie van een overgedragen midi noot. (0- 127)

NoteFreq! = Gronddo! * (2 ^ (byte% / 12))

END FUNCTION

SUB Rbin

Blank1059:LOCATE 10, 10: Kleur 14

PRINT "Give input *.BIN file + path to harmonize";

LOCATE 12, 10: Kleur 13:INPUT "Choice? "; Infile$

Infile$ = UCASE$(Infile$):Infile$ = LTRIM$(RTRIM$(Infile$))

IF RIGHT$(Infile$, 4) <> ".BIN" THEN Infile$ = Infile$ + ".BIN"

i& = 0: j% = 0

OPEN Infile$ FOR BINARY AS #1

REDIM P%(0 TO LOF(1) \ 16, 0 TO 15)

LOCATE 15, 10: Kleur 10

PRINT "Reading "; Infile$; ". Lenght= "; LOF(1); " Wait please...";

k& = 1

WHILE NOT EOF(1)

GET #1, k&, x&

IF x& <> 0 THEN

P%(i&, j%) = x& MOD &H100: x& = (x& - P%(i&, j%)) \ 256

P%(i&, j% + 1) = x& MOD &H100: x& = (x& - P%(i&, j% + 1)) \ 256

P%(i&, j% + 2) = x& MOD &H100: x& = (x& - P%(i&, j% + 2)) \ 256

P%(i&, j% + 3) = x& MOD &H100

ELSE

P%(i&, j%) = 0:P%(i&, j% + 1) = 0

P%(i&, j% + 2) = 0: P(i&,j% + 3) = 0

END IF

k& = k& + 4: ' var is telkens 8 bytes lang

j% = j% + 4: IF j% = 16 THEN i& = i& + 1

j% = j% MOD 16

WEND

CLOSE #1

Kleur 7

END SUB

SUB RTbinder

' Deze Binder werkt in real-time. Nodige input: NuNoot%(0 TO NrVoices%), VorigeNoot%(0 TO NrVoices%)

' De variabele VorigeNoot% wordt aangepast on exit. Hij dient te worden ingezet, voor de noten (NuNoot%()) via midi worden geschreven.

' wanneer een noot gelijk is aan haar voorganger, sla ze dan niet opnieuw aan, maar bind ze met de vorige noot.

' Deze procedure werd reeds veralgemeend tot n-stemmen.Er zit ook kode in voor een minimale melodizering...

FOR ib% = 1 TO NrVoices%

' schrijf de dynamiek zoals in de opgegeven stem:

NuVelo%(ib%) = NuVelo%(0): ' dit kan interaktief zijn cfr.<EGG>,<BOM>,<SONGBOOK> etc...

DO

NuNoot%(ib%) = NuNoot%(ib%) + 12

LOOP UNTIL NuNoot%(ib%) >= Vlow%(ib%)

' leg de nu te spelen noten in het midden van de opgegeven tessituur ' (dit kan verbeterd worden: ipv de kwint als konstante kunnen we vergelijken met Htes%(ib%) - Ltes%(ib%) /2 ipv het vaste interval, kan ook gekeken worden naar het interval tussen vorige en huidige noot.

IF NuNoot%(ib%) AND NuVelo%(ib%) THEN

Ctes% = (Vlow%(ib%) + Vhigh%(ib%)) / 2

SELECT CASE NuNoot%(ib%) - Ctes%

CASE IS > 7

DO

NuNoot%(ib%) = NuNoot%(ib%) - 12

LOOP UNTIL NuNoot%(ib%) - Ctes% <= 7

CASE IS < -7

DO

NuNoot%(ib%) = NuNoot%(ib%) + 12

LOOP UNTIL NuNoot%(ib%) - Ctes >= -7

END SELECT

END IF

' bind nu noten die gelijk zijn aan hun voorganger:

IF VorigeNoot%(ib%) THEN

IF VorigeVelo%(ib%) THEN

IF VorigeNoot%(ib%) = NuNoot%(ib%) THEN

NuNoot%(ib%) = 0: NuVelo%(ib%) = 0

ELSE

VorigeNoot%(ib%) = NuNoot%(ib%):VorigeVelo%(ib%) = NuVelo%(ib%)

END IF

ELSE

VorigeNoot%(ib%) = NuNoot%(ib%):VorigeVelo%(ib%) = NuVelo%(ib%)

END IF

ELSE

VorigeNoot%(ib%) = NuNoot%(ib%):VorigeVelo%(ib%) = NuVelo%(ib%)

END IF

NEXT ib%

END SUB

SUB ShowMatrix ' displays the transposition matrix used.

Blank1059: Kleur 13: vertipos% = 10 :horipos% = 80 - (NrNotes% * 4)

LOCATE vertipos%, horipos%:PRINT "MODULATIEMATRIX:"

FOR il% = 0 TO NrTrans%

FOR ij% = 1 TO NrNotes%

LOCATE vertipos% + il% + 1, horipos% + ((ij% - 1) * 4):PRINT LadMat%(il%, ij%);

NEXT ij%

NEXT il%

END SUB

SUB TestChords

' deze procedure test het voorkomen van nootverdubbelingen in de akkoorden.Dit mag in dit harmonieontwerp immers niet voorkomen.De procedure wordt alleen gebruikt voor het debuggen

LOCATE 40, 10: Kleur 13: PRINT "Teller =";: PRINT USING "########"; i&

LOCATE 41, 10: Kleur 13: PRINT DebugMsg$; " ";:LOCATE 42, 10: Kleur 13: PRINT "Next note = ";

PRINT USING "###"; VolgendeNoot%(0);:LOCATE 43, 10: Kleur 13: PRINT "Tonal Center= ";

PRINT USING "###"; Tc%;

IF NuNoot%(0) > 0 AND NuVelo%(0) > 0 THEN

FOR idb% = 1 TO NrVoices%

IF NuNoot%(idb%) THEN

FOR jdb% = idb% + 1 TO NrVoices%

IF NuNoot%(jdb%) THEN

IF NuNoot%(idb%) MOD 12 = NuNoot%(jdb%) MOD 12 THEN BEEP: GOSUB Druksituatie

END IF

NEXT jdb%

END IF

NEXT idb%

END IF

EXIT SUB

Druksituatie:

Kleur 12: LOCATE 50, 10: PRINT "Tema";:PRINT USING "##"; Stem%(0); : PRINT " =";

PRINT USING "##"; NuNoot%(0) MOD 12;: PRINT " Vel=";:PRINT USING "###"; NuVelo%(0);:

FOR itmp% = 1 TO NrVoices%

LOCATE 50 + itmp%, 10:PRINT "Harm";: PRINT USING "##"; Stem%(itmp%);

PRINT " =";: PRINT USING "##"; NuNoot%(itmp%) MOD 12;

PRINT " Vel=";: PRINT USING "###"; NuVelo%(itmp%);

NEXT itmp%

SLEEP

RETURN

END SUB

SUB Uit (byte%)

WHILE INP(Sp) < 128: dummy% = INP(Dp): WEND

WAIT Sp, 64, 64: OUT Dp, byte%: EXIT SUB

END SUB

SUB Writfil

Blank1059:Logo:\LOCATE 10, 10: Kleur 14:PRINT "Save this choral as a *.bin file? (Y/N)";

DO: k$ = INKEY$: LOOP UNTIL k$ <> ""

IF UCASE$(k$) = "Y" THEN

LOCATE 12, 10: Kleur 14

PRINT "Filename for writing this choral? (no extension)";:Kleur 13: INPUT Uitfile$

LOCATE 14, 10: Kleur 12 :PRINT " Writing bin-file "; Uitfile$; " to disk...";

Lastvoice% = 0

FOR itmp% = 0 TO NrVoices%

IF Lastvoice% < Stem%(itmp%) THEN Lastvoice% = Stem%(itmp%)

NEXT itmp%: ' de stem met het hoogste cijfer staat nu in Lastvoice%

Nul% = 0:k& = 1: ' k& geeft aantal bytes in de file...

OPEN Uitfile$ FOR BINARY AS #1

FOR i& = 0 TO UBOUND(P%, 1)

FOR j% = 0 TO UBOUND(P%, 2)

IF j% < Lastvoice% + 2 THEN

PUT #1, k&, P%(i&, j%): k& = k& + 1

ELSE

PUT #1, k&, Nul%: k& = k& + 1

END IF

NEXT j%

NEXT i&

CLOSE #1

END IF

END SUB


Filedate: 940921

/ 2012-11-26

Terug naar inhoudstafel kursus: <Index Kursus>

Naar homepage dr.Godfried-Willem RAES