'0212082329 maps output of 3D cellular automaton, created by cellstest.exe, to be read from cells12-03-2002.dat to percusssion instrument output '021212 TO DO!!!!!!! belly velocities: read from belly.dat: min required for sound for each note ' tempo controle @ UI ' instrumentselektie @ UI ' drop certain generations ' 'interface: control window + dataview window ' control window: ' 5 (groups of) instruments: troms + thudnerwood + srpingers, klung, vibi, belly, piano ' each with slider for desired min/max polyfony; left click to set min, right click for max ' click on label to turn instrum on/off ' betacurve window: task freq over one generation ' left click on graph to set lowest freq, right click for highest ' horizontal slider: curve panning ' vertical slider: curve steepness 'global fdb as long FUNCTION Cells_init AS LONG DIM CP(4) LOCAL i AS LONG REDIM CLL_Mat(%xdim + 2, %ydim + 2, UBOUND(CP) + 2) AS SINGLE REDIM CLL_FrqMap(%xdim) 'we only need to call getinstrumentparams to get the right channels, we remap notes [0, 35] to CP().notemap(n) GetInstrumentParams CP(0).instrum(0), %IDM_TROMS GetInstrumentParams CP(0).instrum(1), %ID_THUNDERWOOD GetInstrumentParams CP(0).instrum(2), %ID_SPRINGERS GetinstrumentParams CP(1).instrum(0), %ID_KLUNG GetInstrumentParams CP(2).instrum(0), %ID_VIBI GetInstrumentParams CP(3).instrum(0), %ID_BELLY GetInstrumentParams CP(4).instrum(0), %ID_PLAYERPIANO CP(0).instrum(0).naam = "tr/tw/sp" FOR i = LBOUND(CP) TO UBOUND(CP) SetRobotPort CP(i).instrum(0), "", hMidiO() CP(i).instrum(0).lowtes = 0 'remapped according to CP(notemap) CP(i).instrum(0).hightes = 127 CP(i).aan = 0' NEXT SetRobotPort CP(0).instrum(1), "", hMidiO() SetRobotPort CP(0).instrum(2), "", hMidiO() IF ISFALSE Cells_readMappings THEN MSGBOX "error reading cellmappings.dat - can't continue..",%MB_ICONERROR, FUNCNAME$: EXIT FUNCTION CLL_MinFreq = 5: CLL_MaxFreq = 10 CLL_Pan = 1: CLL_Steep = 10 CLL_GetFrqMap CLL_Pan, CLL_Steep task(%Cll_Main).naam = "main" task(%Cll_Main).freq = 15 task(%Cll_Main).cptr = CODEPTR(Cells_MainTask) taskEX(%Cll_main).stopcptr = CODEPTR(CLL_Stop) DIALOG NEW 0, "cells dataview",0,235, 340, 290 TO CLL_Show_hDlg DIALOG SHOW MODELESS CLL_Show_hDlg DIALOG NEW 0, "cells control room",0,0, 220, 220 TO CLL_Ctrl_hDlg DIALOG SHOW MODELESS CLL_Ctrl_hDlg CALL CLL_UIProc ' fdb = freefile ' open "dblog.txt" for output as fdb Modemess CP(3).instrum(0).channel,&H44, 0 'direkt listen task Modemess CP(2).instrum(0).channel, 20, 50 'vibi motors Modemess CP(2).instrum(0).channel, 21, 53 Modemess CP(2).instrum(0).channel, &H40, 1 'vibi no dampers Modemess &H030B, 7, 0 'harma silent FUNCTION = 1 END FUNCTION FUNCTION Cells_MainTask AS LONG STATIC xCount AS LONG STATIC lastgen AS LONG STATIC init AS LONG LOCAL yCount AS LONG LOCAL zCount AS LONG LOCAL tempnote() AS BYTE LOCAL tempval() AS SINGLE LOCAL i AS LONG IF ISFALSE init THEN xcount = Cells_ReadNextGeneration IF ISFALSE xCount THEN MSGBOX "Error reading " + $Cells_Datafile, %MB_ICONERROR, FUNCNAME$ StopTask %Cll_Main EXIT FUNCTION END IF DO WHILE xcount > 94 'addapt this to skip first cells xcount = Cells_ReadNextGeneration LOOP ShowGeneration CLL_Show_hDlg init = %true END IF INCR xCount ' print# fdb, "------xcount: "; xCount IF xCount >= %xdim THEN IF LastGen THEN stoptask %CLL_Main DO xcount = Cells_ReadNextGeneration LOOP WHILE INSTR(CLL_Skip, CHR$(xcount)) ' print# fdb, "-----------------GENERATION"; xcount CONTROL SET TEXT gh.cockpit, %GMT_TEXT0_ID + 10, "Gen Cntdwn" + STR$(xcount) IF ISFALSE xcount THEN lastgen = %true ShowGeneration CLL_Show_hDlg xCount = 0 'becomes 1 on next entry EXIT FUNCTION 'stay silent - give time for reading data END IF 'voor ieder instrument FOR zcount = 0 TO UBOUND(CP) ' print# fdb, "----zcount: "; zcount, CP(zCount).minpoly ; CP(zCount).maxpoly ;CP(zCount).tresh IF CP(zCount).maxpoly < 1 THEN ITERATE FOR CP(zcount).instrum(0).Har(1).vel = STRING$(128, CHR$(0)) IF ISFALSE zcount THEN CP(zcount).instrum(1).Har(1).vel = STRING$(128, CHR$(0)) CP(zcount).instrum(2).Har(1).vel = STRING$(128, CHR$(0)) END IF IF CP(zcount).aan THEN 'otherwise we still playhar the empty harstring for safety.. REDIM tempnote(CP(zcount).maxpoly - 1) 'maxpoly should be at least 2!! REDIM tempval(CP(zcount).maxpoly - 1) 'get highest aant(maxpoly) notes FOR ycount = 0 TO %ydim IF (CLL_MAT(xCount + 1, yCount + 1, zCount + 1) > tempval(0)) THEN POKE$ VARPTR(tempval(1)), PEEK$(VARPTR(tempval(0)), UBOUND(tempval) * 4) POKE$ VARPTR(tempnote(1)), PEEK$(VARPTR(tempnote(0)), UBOUND(tempnote) ) tempval(0) = CLL_MAT(xCount + 1, yCount + 1, zCount + 1) tempnote(0) = yCount + 1 END IF NEXT ARRAY SORT tempval(), TAGARRAY tempnote(), DESCEND ' PRINT# fdb, tempnote() ' print# fdb, tempval() 'now we have stronges maxpoly(@UI) notes for this voice 'retain at least minpoly(@UI) (when possible), + all notes with value >= tresh FOR ycount = 0 TO CP(zcount).maxpoly IF (ycount > CP(zcount).minpoly) AND (tempval(ycount) < CP(zcount).tresh) THEN GOTO Cllplay 'minpoly notes filled in, next note val < treshold IF tempval(ycount) > CP(zcount).abstresh THEN 'map notes and add to new instrhar SELECT CASE zcount CASE 0 'pitchless percussion: fixed velo map, use instrument map AddNote2Har CP(zcount).instrum(CP(zcount).instrmap(tempnote(ycount))).Har(1), CP(zcount).notemap(tempnote(ycount)), CP(zcount).velomap(tempnote(ycount)) CASE 3 'belly: fixed velo 1 > mapped internally to smallest value that sounds AddNote2Har CP(zcount).instrum(0).Har(1), CP(zcount).notemap(tempnote(ycount)), 1 CASE ELSE 'piano, vibi, klung: vel range [minvel - maxvel] AddNote2Har CP(zcount).instrum(0).Har(1), CP(zcount).notemap(tempnote(ycount)), CP(zcount).minvel + tempval(ycount) * (CP(zcount).maxvel - CP(zcount).minvel) END SELECT ELSE GOTO CLLplay 'FOR 'value too small, even if minpoly notes not filled in END IF NEXT END IF CLLplay: 'play new har IF zcount = 0 THEN FOR ycount = 0 TO 2: InstrumPlay CP(zcount).instrum(ycount): NEXT ELSE ShowHar CP(0).instrum(2).Har(1),1,140,2! InstrumPlay CP(zcount).instrum(0) END IF NEXT task(%CLL_Main).freq = CLL_FrqMap(xcount + 1) CONTROL SET TEXT gh.cockpit, %gmt_text0_id + 12, STR$(CLL_FrqMap(xcount + 1)) ' 'compute new task.freq. @UI, boogvorm over generatie END FUNCTION FUNCTION Cells_ReadNextGeneration AS LONG 'returns non zero value if successful and more data following. 0 = can't read anything anymore STATIC init AS LONG STATIC fn AS LONG STATIC dD() AS WORD STATIC dims AS BYTE STATIC startpos AS DWORD ' STATIC CLL_Mat() AS SINGLE LOCAL curpos AS DWORD STATIC lenarr AS DWORD STATIC count AS DWORD LOCAL b AS STRING * 4 LOCAL i AS LONG IF ISFALSE init THEN fn = FREEFILE OPEN $Cells_Datafile FOR BINARY ACCESS READ LOCK WRITE AS fn IF ERRCLEAR THEN EXIT FUNCTION SEEK fn, 1 GET fn, , b 'for some reason get$ fn, 4, b fails here!!!! IF b$ <> "CELL" THEN MSGBOX "data file invalid. can't proceed!!" + b$, %MB_ICONERROR, FUNCNAME$ CLOSE fn FUNCTION = %false EXIT FUNCTION END IF GET fn, , dims IF dims <> 3 THEN MSGBOX "nr of dimensions in datafile doesn't fit!!",%MB_ICONERROR,FUNCNAME$ EXIT FUNCTION END IF REDIM dD(dims - 1) 'as word GET fn,,dd() lenarr = dD(0) IF dD(0) <> %xdim + 3 THEN MSGBOX "y-dim in datafile doesn't fit!!",%MB_ICONERROR, FUNCNAME$ EXIT FUNCTION END IF IF dD(1) <> %ydim + 3 THEN MSGBOX "y-dim in datafile doesn't fit!!",%MB_ICONERROR, FUNCNAME$ EXIT FUNCTION END IF IF dD(2) <> UBOUND(CP) + 2 THEN '?? 2 ????! looks like bug in cellstext.bas MSGBOX "z-dim in datafile doesn't fit!!",%MB_ICONERROR, FUNCNAME$ EXIT FUNCTION END IF FOR i = 1 TO UBOUND(dD) lenarr = lenarr * dD(i) NEXT lenarr = lenarr * 4 IF ISFALSE lenarr THEN EXIT FUNCTION GET fn, ,count startpos = SEEK(fn) init = %true END IF curpos = startpos + (count - 1) * lenarr GET fn,curpos, CLL_MAT() FUNCTION = count IF ISFALSE count THEN CLOSE fn DECR count END FUNCTION FUNCTION Cells_ReadMappings AS LONG LOCAL f AS LONG LOCAL b AS STRING LOCAL id AS LONG LOCAL i AS LONG 'read cellmappings.dat to CP struct ' + get belly notemap form belly.dat: lowest velo possible for note f = FREEFILE OPEN "c:\b\pb\gmt\kristof\cells\cellmappings.dat" FOR INPUT ACCESS READ LOCK WRITE AS f IF ERRCLEAR THEN EXIT FUNCTION DO UNTIL EOF(f) LINE INPUT #f, b b = UCASE$(REMOVE$(b, " ")) IF INSTR("'/\", MID$(b, 1, 1)) THEN ITERATE DO SELECT CASE PARSE$(b, ":", 1) CASE "ID" id = VAL(PARSE$(b, ":", 2)) CASE "NOTEMAP" b = PARSE$(b, ":", 2) FOR i = 0 TO PARSECOUNT(b, ",") - 1 CP(id).notemap(i) = VAL(PARSE$(b, ",", i + 1)) NEXT CASE "INSTRMAP" b = PARSE$(b, ":", 2) FOR i = 0 TO PARSECOUNT(b, ",") - 1 CP(id).instrmap(i) = VAL(PARSE$(b, ",", i + 1)) NEXT CASE "VELOMAP" b = PARSE$(b, ":", 2) FOR i = 0 TO PARSECOUNT(b, ",") - 1 CP(id).velomap(i) = VAL(PARSE$(b, ",", i + 1)) NEXT CASE "POLYMIN" CP(id).minpoly = VAL(PARSE$(b, ":", 2)) CASE "POLYMAX" CP(id).maxpoly = VAL(PARSE$(b, ":", 2)) CASE "TRESH" CP(id).tresh = VAL(PARSE$(b, ":", 2)) CASE "ABSTRESH" CP(id).AbsTresh = VAL(PARSE$(b, ":", 2)) CASE "MINVEL" CP(id).minvel = VAL(PARSE$(b, ":", 2)) CASE "MAXVEL" CP(id).maxvel = VAL(PARSE$(b, ":", 2)) CASE "SKIP" b = PARSE$(b, ":", 2) FOR i = 1 TO PARSECOUNT(b) CLL_Skip = CLL_Skip + CHR$(VAL(PARSE$(b, ",", i))) NEXT CASE "EOF" EXIT LOOP CASE ELSE MSGBOX "Check cellmappings.dat!!" + $CRLF + b,,FUNCNAME$: EXIT FUNCTION END SELECT LOOP CLOSE f FUNCTION = %true END FUNCTION SUB ShowGeneration (hw AS LONG) LOCAL hDC AS LONG LOCAL hBrush AS LONG LOCAL oldBrush AS LONG LOCAL originalbrush AS LONG LOCAL x AS LONG LOCAL y AS LONG LOCAL z AS LONG LOCAL bsy AS LONG LOCAL tmp AS SINGLE hDC = GetDC(hw) DIALOG GET CLIENT hw TO x,y DIALOG UNITS hw, x, y TO PIXELS x, y hBrush = CreateSolidBrush( RGB(150, 150, 150) ): originalbrush = SelectObject(hDc, hBrush) PatBlt hDC, 0, 0, x, y, &H00F00021 '%WHITENESS oldbrush = hBrush z = 1: bsy = 0 '(35 + %pointsiz * %dimY) * (z - 1) draw3planes: FOR x = 1 TO %dimX FOR y = 1 TO %dimY oldBrush = hBrush IF CLL_Mat(x, y, z) > .05 THEN hBrush = CreateSolidBrush(RGB(255 * SQR(CLL_Mat(x, y, z)), 255 * SQR(CLL_Mat(x, y, z + 1)), 255 * SQR(CLL_Mat(x, y, z + 2)))) SelectObject hDC, hBrush: DeleteObject oldBrush Rectangle hDC, 9 + (x - 1) * %pointsiz, bsy + (y - 1) * %pointsiz, 9 + x * %pointsiz, bsy + y * %pointsiz END IF NEXT NEXT IF z = 1 THEN z = 4: bsy = (35 + %pointsiz * %dimY) GOTO draw3planes END IF SelectObject hDC, oldBrush: DeleteObject hBrush ReleaseDC hw, hDC END SUB CALLBACK FUNCTION CLL_UIProc LOCAL i AS LONG LOCAL x AS LONG, y AS LONG LOCAL hDC AS LONG LOCAL hBrush AS LONG LOCAL hPen AS LONG LOCAL oldbrush AS LONG LOCAL oldpen AS LONG LOCAL n AS LONG LOCAL tmp AS SINGLE STATIC sl1x() AS LONG STATIC sl2x() AS LONG STATIC init AS LONG STATIC arx1 AS LONG IF ISFALSE init THEN init = %true DIM sl1x(UBOUND(CP)) DIM sl2x(UBOUND(CP)) FOR i = 0 TO UBOUND(CP) sl1x(i) = 5 + i * (2 * (%sls + %spc) + %spc) sl2x(i) = 5 + %sls + %spc + i * (2 *(%sls + %spc) + %spc) NEXT END IF SELECT CASE CBMSG CASE %WM_SETFOCUS, %CLL_MSG_UPDATESCREEN 'update screen 'prepare hdc = getdc(CBHNDL) hBrush = CreateSolidBrush( RGB(180, 225, 150) ) hPen = CreatePen(%PS_SOLID, 1, RGB(40, 50, 50)) oldbrush = SelectObject(hDc, hBrush) oldpen = Selectobject(hDC, hPEn) settextcolor hDC, RGB(40, 50, 50) DIALOG GET CLIENT CBHNDL TO x,y DIALOG UNITS CBHNDL, x, y TO PIXELS x, y PatBlt hDC, 0, 0, x, y, &H00F00021 '=%PATCOPY '%WHITENESS 'sliders FOR i = 0 TO UBOUND(CP) IF CP(i).aan THEN setbkcolor hdc, RGB(255, 245, 175) ELSE setbkcolor hdc, RGB(175, 175, 175) END IF textout hDC, sl1x(i), 5, BYCOPY(CP(i).instrum(0).naam), 8 Rectangle hDC, sl1x(i), %vsly, sl1x(i) + %sls, %vsly + %sll 'polymin/max Rectangle hDC, sl2x(i), %vsly, sl2x(i) + %sls, %vsly + %sll 'tresh Rectangle hDC, sl1x(i) + .1 * %sls, %vsly + %sll * (%CLL_AbsMaxPoly - CP(i).maxpoly) / %CLL_AbsMaxPoly,_ sl1x(i) + .9 * %sls, %vsly + %sll * (%CLL_AbsMaxPoly - CP(i).minpoly) / %CLL_AbsMaxPoly Rectangle hDC, sl2x(i) + .1 * %sls, %vsly + %sll * (1 - CP(i).abstresh), sl2x(i) + .9 * %sls,_ 1 + %vsly + %sll * (1 - CP(i).tresh) NEXT 'draw freq evol over this gen CLL_DrawFrqMap(hDC) '@freq: beta curve pan slider Rectangle hDC, %spc/2, %tably + %sll + %spc, %spc/2 + UBOUND(CLL_FrqMap) + 1, %tably + %sll + %spc + %sls MoveTo hDC, %spc/2 + (((CLL_Pan - .2)/6)^0.3125) * UBOUND(CLL_FrqMap), %tably + %sll + %spc LineTo hDC, %spc/2 + (((CLL_Pan - .2)/6)^0.3125) * UBOUND(CLL_FrqMap), %tably + %sll + %spc + %sls 'beta curve steepness slider Rectangle hDC, %spc/2 + UBOUND(CLL_FrqMap) + %spc, %tably, %spc/2 + UBOUND(CLL_FrqMap) + %spc + %sls,%tably + %sll MoveTo hDC, %spc/2 + UBOUND(CLL_FrqMap) + %spc, %tably + %sll * ((1 - (CLL_Steep - .4) / %CLL_BetaSteepMax))^2 LineTo hDC, %spc/2 + UBOUND(CLL_FrqMap) + %spc + %sls, %tably + %sll * ((1 - (CLL_Steep - .4) / %CLL_BetaSteepMax))^2 'cleanup SelectObject hDc, oldbrush SelectObject hDC, OldPen deleteobject hBrush deleteobject hPen releasedc CBHNDL, hdc CASE %WM_LBUTTONUP x = LOWRD(CBLPARAM) y = HIWRD(CBLPARAM) IF (y < 17 ) AND (y > 5) THEN FOR i = 0 TO UBOUND(CP) IF (x > sl1x(i)) AND (x < sl2x(i) + %sls + %spc) THEN BIT TOGGLE CP(i).aan, 0 SendMessage CBHNDL, %CLL_MSG_UPDATESCREEN, 0, 0 EXIT SELECT END IF NEXT ELSEIF (y < %vsly + %sll) AND (y > %vsly) THEN FOR i = 0 TO UBOUND(CP) 'polymin? IF (x > sl1x(i)) AND (x < sl1x(i) + %sls) THEN CP(i).minpoly = MAX(1, MIN((%CLL_AbsMaxPoly * (%sll - (y - %vsly)) / %sll), CP(i).maxpoly - 1)) SendMessage CBHNDL, %CLL_MSG_UPDATESCREEN, 0, 0 EXIT FOR 'tresh? ELSEIF (x > sl2x(i)) AND (x < sl2x(i) + %sls) THEN CP(i).abstresh = MIN(.95, MAX(.02, (%sll - (y - %vsly)) / %sll)) IF CP(i).tresh < CP(i).abstresh THEN SWAP CP(i).tresh, CP(i).abstresh SendMessage CBHNDL, %CLL_MSG_UPDATESCREEN, 0, 0 EXIT FOR END IF NEXT ELSEIF (y < %tably + %sll) AND (y > %tably) THEN 'change minfreq by clicking in graph IF (x < %spc/2 + UBOUND(CLL_FrqMap)) AND (x > %spc/2) THEN CLL_MinFreq = %CLL_AbsMinFreq + %CLL_AbsMaxFreq * (%tably + %sll - y) / %sll CLL_GetFrqMap CLL_Pan, CLL_Steep SendMessage CBHNDL, %CLL_MSG_UPDATESCREEN, 0, 0 ELSEIF (x > %spc/2 + UBOUND(CLL_FrqMap) + %spc) AND (x < %spc/2 + UBOUND(CLL_FrqMap) + %spc + %sls) THEN 'steepness of betacurv slider CLL_Steep = .4 + %CLL_BetaSteepMax * (1 - SQR((y - %tably) / %sll)) CLL_GetFrqMap CLL_Pan, CLL_Steep SendMessage CBHNDL, %CLL_MSG_UPDATESCREEN, 0, 0 END IF ELSEIF (y < %tably + %sll + %spc + %sls ) AND (y > %tably + %sll + %spc) THEN IF (x < %spc/2 + UBOUND(CLL_FrqMap)) AND (x > %spc/2) THEN 'pan of betacurve slider tmp = (x - %spc/2) / UBOUND(CLL_FrqMap) CLL_Pan = .2 + 6 * tmp ^ 3.2 CLL_GetFrqMap CLL_Pan, CLL_Steep SendMessage CBHNDL, %CLL_MSG_UPDATESCREEN, 0, 0 END IF END IF CASE %WM_RBUTTONUP x = LOWRD(CBLPARAM) y = HIWRD(CBLPARAM) IF (y < %vsly + %sll) AND (y > %vsly) THEN 'polymax? FOR i = 0 TO UBOUND(CP) IF (x > sl1x(i)) AND (x < sl1x(i) + %sls) THEN CP(i).maxpoly = MIN(%CLL_AbsMaxPoly, MAX(2, %CLL_AbsMaxPoly * (%sll - (y - %vsly)) / %sll, CP(i).minpoly + 1)) SendMessage CBHNDL, %CLL_MSG_UPDATESCREEN, 0, 0 EXIT FOR ELSEIF (x > sl2x(i)) AND (x < sl2x(i) + %sls) THEN CP(i).tresh = MIN(.95, MAX(.02, (%sll - (y - %vsly)) / %sll)) IF CP(i).tresh < CP(i).abstresh THEN SWAP CP(i).tresh, CP(i).abstresh SendMessage CBHNDL, %CLL_MSG_UPDATESCREEN, 0, 0 EXIT FOR END IF NEXT ELSEIF (y < %tably + %sll) AND (y > %tably) THEN 'change maxfreq by rightclicking in graph IF (x < %spc/2 + UBOUND(CLL_FrqMap)) AND (x > %spc/2) THEN CLL_MaxFreq = %CLL_AbsMinFreq + %CLL_AbsMaxFreq * (%tably + %sll - y) / %sll CLL_GetFrqMap CLL_Pan, CLL_Steep SendMessage CBHNDL, %CLL_MSG_UPDATESCREEN, 0, 0 END IF END IF CASE %WM_CLOSE init = %false END SELECT END FUNCTION SUB CLL_GetFrqMap(pan AS SINGLE, steep AS SINGLE) LOCAL a AS SINGLE LOCAL b AS SINGLE LOCAL normval AS SINGLE LOCAL i AS LONG LOCAL x AS SINGLE a = pan * steep b = steep/pan normval = (a^a * b^b/(a + b)^(a + b)) FOR i = 0 TO UBOUND(CLL_FrqMap) x = i / UBOUND(CLL_FrqMap) CLL_FrqMap(i) = CLL_MinFreq + (CLL_MaxFreq - CLL_MinFreq) * (x ^ a * (1 - x) ^b)/normval NEXT END SUB SUB CLL_DrawFrqMap(hDC AS LONG) LOCAL x AS LONG LOCAL oldpoint AS POINTL LOCAL hBrush AS LONG LOCAL hPen AS LONG hBrush = CreateSolidBrush(RGB(75, 75, 75)): hPen = CreatePen(%PS_SOLID, 1, RGB(255, 150, 0)) hBrush = SelectObject(hDC, hBrush) Rectangle hDC, %spc/2, %tably, %spc/2 + UBOUND(CLL_FrqMap) + 1, %tably + %sll + 1 hPen = SelectObject(hDC, hPen) MoveToEx hDC, %spc/2, %tably + %sll - %sll * (CLL_FrqMap(0) - %CLL_AbsMinFreq) / %CLL_AbsMaxFreq , oldpoint FOR x = 1 TO UBOUND(CLL_FrqMap) LineTo hDC, %spc/2 + x, %tably + %sll - %sll * (CLL_FrqMap(x) - %CLL_AbsMinFreq) / %CLL_AbsMaxFreq NEXT hBrush = SelectObject(hDC, hBrush): hPen = SelectObject(hDC, hPen) deleteObject hBrush: DeleteObject hPen END SUB SUB CLL_Stop LOCAL n AS BYTE FOR n = CP(4).instrum(0).lowTes TO CP(4).instrum(0).lowTes NoteOff CP(4).instrum(0).channel, n SLEEP 10 NEXT END SUB