A Track-Creating Program Written In Qb64

This is a BASIC program I wrote for use with RENOISE. It invents a single track with random note commands and other things. Technically, it invents a XML-like file that is copied into the Windows clipboard and that could be pasted into a track in RENOISE.

After the program is compiled and run, it will bring about a window with a black background… there is no output displayed. But you should have this program and RENOISE running in your Windows system session. You run this program, then quickly switch to RENOISE and find an empty track, then press [CTRL][V] or choose “Paste” from the Edit menu.

I spent the longest making sure the formatting (spaces) of the clipboard data went down correctly.

This was written for QB64, which is a stupendous effort by Galleon. It was based on QBASIC but meant to work on 32-bit and 64-bit systems.

Download (freeware):

http://www.qb64.net/forum/index.php?board=2.0

Caveats:

(1) The program assumes 128 rows in a pattern, and only one note column and only one effect column. I’m sorry, I had to make it as simple as possible. :)

(2) The panning commands aren’t accounted for. It would be fairly easy to include that capability – but the logic in the program gets tricky around line 44!

(3) The subprogram section which starts with the line label “putinnotes” is where the program could be customized to the user’s taste, that is, how to insert note commands, effect commands etc. The routine actually in the program just chooses every 4th row and decides whether or not it should have a note command. If so, it probably gives it a volume command too, and much less often, an offset command. A few rows after that, a NOTE-OFF is inserted. Only instrument #00 is used in the routine.

I might as well break down the user-defined type which handles the tracker pattern data. It’s really straightforward:

s(i).noat Note number (0-127)
s(i).nstru Instrument number *
s(i).vol Volume column
s(i).dlay Delay column
s(i).efn The pattern effect number (gapper, offset etc.) *
s(i).efv The pattern effect value

  • Neither the instrument number nor the pattern effect number should be -1. That’s the value used by the program to compare to see if there’s anything in a certain row.

(4) The QB64 compiler is slow, yes I know, but otherwise I could have written this in PureBASIC, and I’m much less comfortable using that other product (it’s not even installed in my computers anymore). QB64 is also peculiar about where a *.EXE file it created could be placed in the system. But the best part about this is that it could be customized! You would have to know how to program in BASIC, though. But changing things could be rewarding… ;)

“Legal” stuff: if you use this program and the QB64 compiler, I’m not responsible for your system’s performance, or your inability to use the programs etc.
I give away the source code so that a few people using RENOISE could get creative. It’s not meant at all to be a prank or something else that should have disgusted someone. This is a program that I wrote, I don’t have to rip off anyone else for anything because I programmed in BASIC on a PC since 1989 and have spent many years refining text-processing routines. You could see that this program doesn’t do 3D graphics or sound or anything like that. Basically it does what I just said, text processing, but it required a nifty feature (copying data to the Windows clipboard).

ONE MORE THING: This program was meant to be used with RENOISE v2.0 and later (because of the delay column).

Enjoy! :D

Francisco
March 20, 2010

DEFINT A-Z  
  
const NUMROW = 128  
  
TYPE trakhtype  
noat AS INTEGER  
nstru AS INTEGER  
vol AS INTEGER  
dlay AS INTEGER  
efn AS INTEGER  
efv AS INTEGER  
END TYPE  
  
DIM s(1 TO Numrow) AS trakhtype  
  
lf$ = CHR$(10)  
cr$ = CHR$(13)  
'lf$ = CHR$(13) + CHR$(10) 'FOR TESTING PURPOSES ONLY  
qu$ = CHR$(34)  
  
o$ = ""  
  
RANDOMIZE  
  
GOSUB initrows  
GOSUB putinnotes  
  
o$ = o$ + "<?xml version=" + qu$ + "1.0" + qu$ + " encoding=" + qu$ + "UTF-8" + qu$ + "?>" + lf$  
o$ = o$ + "<patternclipboard.blockbuffer doc_version=" + qu$ + " qu>" + slf$(2)<br>
o$ = o$ + "<trackcolumns>" + slf$(2)<br>
o$ = o$ + "<trackcolumn>" + slf$(2)<br>
o$ = o$ + "<trackcolumn>" + slf$(2)<br>
o$ = o$ + "<lines>" + slf$(2)<br>
<br>
FOR i = 1 TO Numrow<br>
<br>
    IF s(i).nstru &lt;&gt; -1 THEN<br>
        o$ = o$ + "<line index=" + qu$ + LTRIM$(STR$(i - 1)) + qu$ + ">" + slf$(2)<br>
        o$ = o$ + "<notecolumns>" + slf$(2)<br>
        o$ = o$ + "<notecolumn>" + slf$(2)<br>
        o$ = o$ + "<note>" + notenum2name$(s(i).noat) + "</note>" + slf$(0)<br>
        o$ = o$ + "<instrument>" + duohex$(s(i).nstru) + "</instrument>"<br>
        IF s(i).vol &gt; 0 THEN<br>
            o$ = o$ + slf$(0) + "<volume>" + duohex$(s(i).vol) + "</volume>"<br>
            IF s(i).dlay &gt; 0 THEN<br>
                o$ = o$ + slf$(0) + "<delay>" + duohex$(s(i).dlay) + "</delay>" + slf$(-2)<br>
            ELSE<br>
                o$ = o$ + slf$(-2)<br>
            END IF<br>
        ELSE<br>
            o$ = o$ + slf$(-2)<br>
        END IF<br>
        o$ = o$ + "</notecolumn>" + slf$(-2)<br>
        o$ = o$ + "</notecolumns>" + slf$(-2)<br>
        o$ = o$ + "</line>" + slf$(0)<br>
    ELSEIF s(i).noat = 255 THEN<br>
        o$ = o$ + "<line index=" + qu$ + LTRIM$(STR$(i - 1)) + qu$ + ">" + slf$(2)<br>
        o$ = o$ + "<notecolumns>" + slf$(2)<br>
        o$ = o$ + "<notecolumn>" + slf$(2)<br>
        o$ = o$ + "<note>OFF</note>" + slf$(-2)<br>
        o$ = o$ + "</notecolumn>" + slf$(-2)<br>
        o$ = o$ + "</notecolumns>" + slf$(-2)<br>
        o$ = o$ + "</line>" + slf$(0)<br>
    ELSE<br>
        o$ = o$ + "<line index=" + qu$ + LTRIM$(STR$(i - 1)) + qu$ + "></line>"<br>
        IF i = Numrow THEN<br>
            o$ = o$ + slf$(-2)<br>
        ELSE<br>
            o$ = o$ + slf$(0)<br>
        END IF<br>
    END IF<br>
<br>
NEXT i<br>
<br>
o$ = o$ + "</lines>" + slf$(0)<br>
o$ = o$ + "<columntype>NoteColumn</columntype>" + slf$(-2)<br>
o$ = o$ + "</trackcolumn>" + slf$(0)<br>
o$ = o$ + "<trackcolumn>" + slf$(2)<br>
o$ = o$ + "<lines>" + slf$(2)<br>
<br>
FOR i = 1 TO Numrow<br>
    IF s(i).efn &lt;&gt; -1 THEN<br>
        o$ = o$ + "<line index=" + qu$ + LTRIM$(STR$(i - 1)) + qu$ + ">" + slf$(2)<br>
        o$ = o$ + "<effectcolumns>" + slf$(2)<br>
        o$ = o$ + "<effectcolumn>" + slf$(2)<br>
        o$ = o$ + "<value>" + duohex$(s(i).efv) + "</value>" + slf$(0)<br>
        o$ = o$ + "<number>" + duohex$(s(i).efn) + "</number>" + slf$(-2)<br>
        o$ = o$ + "</effectcolumn>" + slf$(-2)<br>
        o$ = o$ + "</effectcolumns>" + slf$(-2)<br>
        o$ = o$ + "</line>" + slf$(0)<br>
    ELSE<br>
        o$ = o$ + "<line index=" + qu$ + LTRIM$(STR$(i - 1)) + qu$ + "></line>"<br>
        IF i = Numrow THEN<br>
            o$ = o$ + slf$(-2)<br>
        ELSE<br>
            o$ = o$ + slf$(0)<br>
        END IF<br>
    END IF<br>
NEXT i<br>
<br>
o$ = o$ + "</lines>" + slf$(0)<br>
o$ = o$ + "<columntype>EffectColumn</columntype>" + slf$(-2)<br>
o$ = o$ + "</trackcolumn>" + slf$(-2)<br>
o$ = o$ + "</trackcolumn>" + slf$(-2)<br>
o$ = o$ + "</trackcolumns>" + slf$(-2)<br>
o$ = o$ + "</patternclipboard.blockbuffer>" + lf$  
  
_CLIPBOARD$ = o$  
  
pend:  
END  
  
'******************************  
'put in notes routine goes here  
'******************************  
  
putinnotes:  
vl$ = "46844682646824866468264866824"  
vll = LEN(vl$)  
m1 = 1  
m2 = 1  
FOR j = 1 TO Numrow - 4 STEP 4  
 IF RND > .667 THEN  
 k = j + INT(RND * 3 + 1)  
 s(j).noat = 48  
 s(j).vol = VAL("&H" + MID$(vl$, m1, 1) + "0")  
 IF s(j).vol >= 128 THEN s(j).vol = 0  
 m1 = m1 + 1  
 IF m1 > vll THEN m1 = 1  
 s(j).nstru = 0  
 IF RND > .85 THEN  
 s(j).dlay = &H80  
 ELSEIF RND > .75 THEN  
 s(j).efn = 9  
 s(j).efv = VAL("&H" + MID$(vl$, m2, 1) + "0")  
 m2 = m2 + 1  
 IF m2 > vll THEN m2 = 1  
 END IF  
 s(k).noat = 255  
 END IF  
NEXT j  
RETURN  
  
initrows:  
FOR i = 1 TO Numrow  
 s(i).noat = 0  
 s(i).nstru = -1  
 s(i).vol = 0  
 s(i).dlay = 0  
 s(i).efn = -1  
 s(i).efv = 0  
NEXT i  
RETURN  
  
'---------------------------------------------------------------------------  
'SUBs  
'---------------------------------------------------------------------------  
  
FUNCTION notenum2name$ (num)  
oc = FIX(num / 12)  
semit = num MOD 12  
SELECT CASE semit  
 CASE 0: a$ = "C-"  
 CASE 1: a$ = "C#"  
 CASE 2: a$ = "D-"  
 CASE 3: a$ = "D#"  
 CASE 4: a$ = "E-"  
 CASE 5: a$ = "F-"  
 CASE 6: a$ = "F#"  
 CASE 7: a$ = "G-"  
 CASE 8: a$ = "G#"  
 CASE 9: a$ = "A-"  
 CASE 10: a$ = "A#"  
 CASE 11: a$ = "B-"  
END SELECT  
a$ = a$ + CHR$(48 + oc)  
notenum2name$ = a$  
END FUNCTION  
  
FUNCTION duohex$ (decnum)  
a$ = HEX$(decnum)  
IF decnum < 16 THEN a$ = "0" + a$  
duohex$ = a$  
END FUNCTION  
  
FUNCTION slf$ (indent) STATIC  
stb = stb + indent  
IF stb < 0 THEN stb = 0  
a$ = lf$ + SPACE$(stb)  
slf$ = a$  
END FUNCTION  

Interesting, thanks for your efforts! :)

I thought QB was dead long time…
Long time no .bas code seen… I think you would like it if the Renoise scripting facility gets released… you won’t be needing QB anylonger to do these kind of things.

I did checked the 64-bit variant of QB for one specific thing that made me decide to say goodbye to QB back in '96…
“Array sizes are limited to 32767 Integer elements”
I’m pretty perplex this limit still counts as of today…
Over 20 years and still do we have to take care we cramm everything into 32767 elements…

QB64 supports any amount of integer elements depending only the memory available on your system. If any documentation says otherwise then it should be changed to reflect fact.

It would be interesting to know where you heard that qb64 only supports arrays of 32767 or less elements, I have personally created arrays in QB64 which are several megabytes.

The point of QB64 is to have QB on a modern system without the memory problems of the old days, and this has so far been accomplished.

Yeah, that was quite nostalgic to see code listings done in BASIC again. :)

I got that quote directly from their site itself and it is frankly not about memory but simply how many array elements (records) you can stack in one array.

I had created a database program in QB 4.5 once that had to access databases containing over 32767 records which required various “buffering” algorithms to get stuff done for stuff that did not even consumed 750K of memory.
That was not really funny to do, specially because it was not really about a lot of data (Yes i used an XMM library to access extended memory)

I banked over to XBasic somewhere in '98 and it still fullfills quite some needs for me if i quickly need to whip up something out of my sleeves.
In some cases Python (regarding networking) does the better job though.

It would be nice to know exactly where you got the information on the site (qb64.net), cause I can tell the author to change the information if so. If it was in the wiki then I can change it myself. I understood that you meant array elements, thus I have had several millions of elements in one array so I know it isn’t limited to 32k.

Since QB64 is always improved this could have been an old issue though. I can tell you for sure that this isn’t an issue at this stage of development.

http://qb64.net/wiki…php?title=BLOAD

http://qb64.net/wiki…php?title=BSAVE

http://qb64.net/wiki…hp?title=VARPTR

Perhaps i should have looked more carefully into the context…
BSAVE and BLOAD seem to require VARPTR and VARPTR has this 32767 limit while #PUT and #GET don’t.
Are BSAVE and BLOAD only still present for compatability reasons then?

types in program

Yes, BSAVE and BLOAD are mostly used for compatibility reasons (and because some people are used to it and require no more).

I asked the author of QB64 for the actual limits:
2147483647

But, by the end of the year it’ll be (only available on 64-bit systems):
9223372036854775807

Also a new statement will come into the next release; REDIM _PRESERVE which allows you to redimension an array without changing the information within it (which will be very useful).

Nice… finally a non-destructional redim.
Well, I won’t step away from XB anylonger, QB is past time now, but good to see it is still evolving and good to see M$ is not behind it :).

I looked at XBasic and realised I had tested it before, it seems like a really good language! No language can suit all needs, QB64 fulfills my needs, glad you have found something that fulfills yours! (now there’s a diplomatic answer :)), kidding aside I meant every word though!

@Cyperium

This little code from you was interesting:

http://www.qb64.net/forum/index.php?topic=866.0

Basically (no pun intended) because it seems useful for automatically writing clipboard xml data to disc for further manipulation by other scripts.

Thanks, yes it can be useful in many ways :), I’m using it to copy the syntax of each statement to include in a help file later (which would be a very timetaking task without the program), I hope you find use for it and you can alter it in any way that suits you!