DECLARE SUB CAKETIME (MEASBEATTICK$, AdLibEventStartTime%)
DECLARE SUB OPENFILE (BUFNUM%, MODE$, DEFAULT$, A$, DAFLAG%, EXT$)
DECLARE SUB FILESPEC (DR$, NAME$, FILENAME$)
DECLARE SUB ANYKEY (ZA$)
DECLARE SUB INPUTUCASE (STRNG$)
OPTION BASE 1

REM $INCLUDE: 'COMDEF.BAS'

DIM HEADER AS STRING * 48
DIM TPB AS STRING * 1
DIM BPM AS STRING * 1
DIM BYTE AS STRING * 1
DIM TEMP2 AS STRING * 2
DIM TEMP4 AS STRING * 4
DIM TEMP11 AS STRING * 11
DIM AdLibNoteData AS STRING * 4


SIZE& = FRE(-1)  'Dynamically allocate array size for users memory
SIZE& = (SIZE& - 1000) / 8 'Need 1000 bytes for rest of variables
IF SIZE& > 32767 THEN SIZE& = 32767

DIM TrackNum(SIZE&) AS INTEGER             'Track number
DIM AdLibEventStartTime(SIZE&) AS INTEGER  'Time the event started (in AdLib ticks).
                                           'Measured from the start of the song.
DIM CakeNote(SIZE&) AS INTEGER             'MIDI note number
DIM CakeDur(SIZE&) AS INTEGER              'Duration of the event (in Cakewalk ticks)
        SCREEN 0
        MESSAGE% = 10   'Color used for printing messages and prompts
        EMPH% = 12       'Emphasized color for messages and prompts
        DATACOLOR% = 11      'Color used for entering data
        COLOR EMPH%, 0
        CLS
        PRINT
        PRINT
        PRINT
        PRINT
        PRINT "                          A D L 2 C A K E"
        PRINT
        COLOR MESSAGE%, 0
        PRINT "                            Version 1.0"
        PRINT
        PRINT "                           Nov. 1, 1988"
        PRINT
        PRINT
        PRINT
        PRINT
        PRINT
        PRINT "                    Written by Fred A. McGiven"
        PRINT "                         CIS <70325,520>"
        PRINT
        PRINT
        PRINT
        PRINT
        PRINT "              With the amount of memory in your machine:"
        PRINT "           you are limited to a total of"; SIZE&; "note events."
        PRINT
        PRINT
        CALL ANYKEY(AN$)
        FI1$ = "FILE NAME (ENTER = "
        FI2$ = ", A to Abort)"
       
        CLS
        PRINT "This program reads a song data file created by Ad Lib's (TM) Visual Composer"
        PRINT "and converts it to a Cakewalk ASCII file. After the conversion you"
        PRINT "will need to use the ASC2CAKE conversion utility furnished with"
        PRINT "Cakewalk to make the file usable in Cakewalk. You may have to deselect"
        PRINT "the Ad Lib percusion tracks in Cakewalk. This program only supports"
        PRINT "conversion of note data, i.e. it does not look at volume or tempo data."
        PRINT
        PRINT "Ad Lib Inc. makes a plug in synthesizer card and a number of related software"
        PRINT "packages for the IBM PC/XT/AT or compatible. If you have a high speed computer"
        PRINT "you may need to set it to a lower speed in order to make it work properly."
       
        PRINT
        PRINT "Cakewalk is made by Twelve Tone Systems and is Copyrighted by Greg Hendershot."
        PRINT "Cakewalk is a MIDI sequencer program that allows you to record, edit, and play"
        PRINT "music on your synthesizer/keyboard. You will need a Roland MPU-401, MPU-IPC,"
        PRINT "or equivalent along with a MIDI instrument in order to use this program."
        PRINT
        PRINT "Feel free to make any modifications you want to this program. If there are any"
        PRINT "energetic programmers out there who improve on this program please upload your"
        PRINT "your new masterpiece for others to share. I hope you enjoy this program."
        PRINT
        PRINT
        CALL ANYKEY(AN$)
        ADLIBFILE$ = "TEST.ROL"
        CAKEWALKFILE$ = "TEST.ASC"

START:
        BUFNUM% = 1
        MODE$ = "B"
        CLS
        PRINT "Name of the AdLib file to be converted (usually has .ROL extension)"
        DAFLAG% = 0
        EXT$ = ".ROL"
        CALL OPENFILE(BUFNUM%, MODE$, ADLIBFILE$, A$, DAFLAG%, EXT$)
        IF A$ = "N" THEN
                CLOSE
                CLS
                GOTO TRYAGAIN
        END IF

        BUFNUM% = 2
        MODE$ = "O"
        PRINT
        PRINT "Name of the new Cakewalk file (normally has .ASC extension)"
        DAFLAG% = 0
        EXT$ = ".ASC"
        I% = INSTR(ADLIBFILE$, ".")
        IF I% > 1 THEN
                CAKEWALKFILE$ = LEFT$(ADLIBFILE$, I% - 1) + EXT$
        END IF
        CALL OPENFILE(BUFNUM%, MODE$, CAKEWALKFILE$, A$, DAFLAG%, EXT$)
        IF A$ = "N" THEN
                CLOSE
                GOTO TRYAGAIN
        END IF
        GET #1, 1, HEADER          'Get first 48 bytes from the file
        TPB$ = MID$(HEADER, 45, 1) 'Read timimg info from AdLib file
        BPM$ = MID$(HEADER, 47, 1)
        TicksPerBeat% = ASC(TPB$)
        BeatsPerMeasure% = ASC(BPM$)
        TicksPerMeasure% = TicksPerBeat% * BeatsPerMeasure%
       
        TRACK% = 0              'Initialization
        AdLibTickDur% = 0
        FOUNDTRACK% = 0
        EVENT% = 0

NEXTTRACK:                      'Read AdLib Visual Composer file
                                'Find ?Voix in the file
        EXITFLAG% = 0
        PRINT "Searching for track data"
        DO
                GET #1, , BYTE$              'Get 1 byte from the file
                IF BYTE$ = "?" THEN
                        GET #1, , TEMP4$     'Get next four bytes
                                IF TEMP4$ = "Voix" THEN
                                        EXITFLAG% = 1
                                END IF
                END IF
        LOOP UNTIL EXITFLAG% = 1 OR EOF(1)
        IF EOF(1) THEN
                IF FOUNDTRACK% = 1 THEN
                        PRINT
                        PRINT "Sorting all tracks by time"
                        GOSUB SortByStartTime
                        PRINT #2, "[VARIABLES]"
                        PRINT #2, "Tempo=120"
                        PRINT #2, "Tempo1=60"
                        PRINT #2, "Tempo2=120"
                        PRINT #2, "Tempo3=240"
                        TIM$ = RTRIM$(LTRIM$(STR$(BeatsPerMeasure%)))
                        PRINT #2, "Timesig="; TIM$; "/4"
                        PRINT #2, "Keysig=0"
                        PRINT #2,
                        PRINT #2, "[TRACKS]"
                        FOR I% = 1 TO TRACK%
                                PRINT #2, LTRIM$(STR$(I%)); " "; CHR$(34); "Track"; I%; CHR$(34); " PLAY 0 0 0"
                        NEXT
                        PRINT #2,
                        PRINT #2, "[EVENTS]"
                        FOR I% = 1 TO EVENT%
                                PRINT #2, LTRIM$(STR$(TrackNum(I%))); " 1 "; 'Track & MIDI channel (1)
                                CALL CAKETIME(MEASBEATTICK$, AdLibEventStartTime(I%))
                                PRINT #2, MEASBEATTICK$; " N";
                                PRINT #2, CakeNote(I%);
                                PRINT #2, "64";    'Velocity
                                PRINT #2, CakeDur(I%)
                        NEXT
                        PRINT #2,
                        PRINT #2, "[END]"
                ELSE
                        PRINT "Conversion NOT successful!"
                END IF
                CLOSE
                GOTO TRYAGAIN
        END IF
        'If Voix is found program drops through to here
        GET #1, , TEMP11$        'Discard next 11 bytes
        GET #1, , TEMP2$         'Read total number of ticks on the AdLib track
        'TRACKTICKS% - total number of ticks on the AdLib track
        TRACKTICKS% = ASC(MID$(TEMP2$, 1, 1)) + 256 * ASC(MID$(TEMP2$, 2, 1))
        IF TRACKTICKS% = 0 THEN GOTO NEXTTRACK'If no data for the track go on to the next one
        TRACK% = TRACK% + 1
        TOTALTICKS% = 0 ' counter to keep track of how many trackticks have been processed
        PRINT "Reading track number "; TRACK%
       
        DO              ' Loop to read and process the track note/duration data
                EVENT% = EVENT% + 1             'EVENT% = number of events (all tracks combined)
                GET #1, , AdLibNoteData$         'Get four bytes of note data
                CakeNote(EVENT%) = ASC(MID$(AdLibNoteData$, 1, 1))
                AdLibTickDur% = ASC(MID$(AdLibNoteData$, 3, 1)) + 256 * ASC(MID$(AdLibNoteData$, 4, 1))
                AdLibEventStartTime(EVENT%) = TOTALTICKS%
                TOTALTICKS% = TOTALTICKS% + AdLibTickDur%
                BM! = BeatsPerMeasure%
                ADLTD! = AdLibTickDur%
                CakeDur(EVENT%) = INT(.5 + 120 / TicksPerBeat% * ADLTD!)
                'Subtract one from duration (either Cakewalk or my synthesizer
                'doesn't like one note starting exactly when another ends)
                IF CakeDur(EVENT%) > 15 THEN CakeDur(EVENT%) = CakeDur(EVENT%) - 1
                TrackNum(EVENT%) = TRACK%
                IF CakeNote(EVENT%) = 0 THEN EVENT% = EVENT% - 1  'Discard if no note is being played
        LOOP UNTIL TOTALTICKS% >= TRACKTICKS%
       
        FOUNDTRACK% = 1
        GOTO NEXTTRACK

TRYAGAIN:
        INPUT "Do you want to convert another (Y/N)"; AN$
        AN$ = LTRIM$(UCASE$(AN$))
        IF AN$ = "" THEN AN$ = "Y"
        IF LEFT$(AN$, 1) = "Y" THEN GOTO START
        STOP

SortByStartTime:
        J = 2
        K = 1
        M = EVENT%
        WHILE J > K
                M = INT(M / 2)
                IF M = 0 THEN
                        TEMP = FRE("")
                        RETURN
                ELSE
                        J = 1
                        K = EVENT% - M
                END IF
                WHILE J <= K
                        I = J
                        LOOPFLAG% = 1
                        WHILE LOOPFLAG% = 1
                                L = I + M
                                IF AdLibEventStartTime(I) >= AdLibEventStartTime(L) THEN
                                        SWAP TrackNum(I), TrackNum(L)
                                        SWAP AdLibEventStartTime(I), AdLibEventStartTime(L)
                                        SWAP CakeNote(I), CakeNote(L)
                                        SWAP CakeDur(I), CakeDur(L)
                                        I = I - M
                                        IF I >= 1 THEN
                                                LOOPFLAG% = 1
                                        ELSE
                                                LOOPFLAG% = 0
                                        END IF
                                ELSE
                                        LOOPFLAG% = 0
                                END IF
                        WEND
                J = J + 1
                WEND
        WEND
        RETURN


FILEHANDLER:
        IF ERR <> OLDERR% THEN
                CLS
                PRINT "FILE Error "; ERR
                CALL ANYKEY(AN$)
                CLS
                OLDERR% = ERR
        END IF
        ERRFLAG% = ERR
        RESUME NEXT

END

SUB ANYKEY (ZA$)
        PRINT "Hit any key to continue"
        ZA$ = ""
        WHILE ZA$ = ""
                ZA$ = INKEY$
        WEND
END SUB

SUB CAKETIME (MEASBEATTICK$, AdLibEventStartTime%)
SHARED BeatsPerMeasure%, TicksPerBeat%, TicksPerMeasure%
'This subprogram converts a AdLib event starting time (in Ad Lib ticks)
'to the form of..... measure:beat:tick   that Cakewalk wants to see.
        NUMMEASURES% = INT(AdLibEventStartTime% / TicksPerMeasure% + .01)
        TICKSLEFT% = AdLibEventStartTime% - NUMMEASURES% * TicksPerMeasure%
        NUMBEATS% = INT(TICKSLEFT% / TicksPerBeat% + .01)
        TICKSLEFT% = TICKSLEFT% - NUMBEATS% * TicksPerBeat%
        ADLTD! = TICKSLEFT%
        NUMTICKS% = INT(.5 + 120 / (TicksPerBeat%) * ADLTD!)
        MEAS$ = LTRIM$(RTRIM$(STR$(NUMMEASURES% + 1)))
        BEAT$ = LTRIM$(RTRIM$(STR$(NUMBEATS% + 1)))
        TICK$ = LTRIM$(RTRIM$(STR$(NUMTICKS%)))
        MEASBEATTICK$ = MEAS$ + ":" + BEAT$ + ":" + TICK$
END SUB

SUB FILESPEC (DR$, NAME$, FILENAME$) STATIC
              
REM     January 10, 1987
              
        ON ERROR GOTO FILEHANDLER
        IF INSTR(NAME$, ":") <> 0 OR INSTR(NAME$, "\") <> 0 THEN
                FILENAME$ = NAME$
        ELSE
                TEMP$ = RIGHT$(DR$, 1)
                IF TEMP$ = ":" OR TEMP$ = "\" THEN
                        FILENAME$ = DR$ + NAME$
                ELSE
                        IF DR$ <> "" THEN
                                FILENAME$ = DR$ + "\" + NAME$
                        ELSE
                                FILENAME$ = NAME$
                        END IF
                END IF
        END IF
END SUB

SUB INPUTUCASE (STRNG$) STATIC
        INPUT STRNG$
        STRNG$ = UCASE$(STRNG$)
END SUB

SUB OPENFILE (BUFNUM%, MODE$, DEFAULT$, A$, DAFLAG%, EXT$) STATIC
               
REM     October 30, 1988

        SHARED MESSAGE%, EMPH%
        SHARED DRIVE$
               
        ON ERROR GOTO FILEHANDLER
        ERRFLAG% = 0
        FI1$ = "FILE NAME (ENTER = "
        FI2$ = ", A to Abort)"
        CR$ = CHR$(13)
        IF ERRFLAG% <> 0 THEN GOTO OPENEXIT

OPENFILESTART:
        IF DAFLAG% = 1 THEN
                A$ = DEFAULT$
                GOTO DONTASK
        END IF
        SCREEN 0, 1
        COLOR MESSAGE%, 0
        PRINT FI1$;
        COLOR EMPH%, 0
        IF INSTR(DEFAULT$, ".") = 0 THEN DEFAULT$ = DEFAULT$ + EXT$
        PRINT DEFAULT$;
        COLOR MESSAGE%, 0
        PRINT FI2$;
        CALL INPUTUCASE(A$)
        IF A$ = CR$ OR A$ = "" THEN A$ = DEFAULT$
        IF A$ = "A" OR ERRFLAG% <> 0 THEN
                A$ = "N"
                GOTO OPENEXIT
        END IF
        IF INSTR(A$, ".") = 0 THEN A$ = A$ + EXT$
        DEFAULT$ = A$
DONTASK:
        CALL FILESPEC(DRIVE$, A$, FILENAME$)
        OPEN "R", BUFNUM%, FILENAME$
        IF LOF(BUFNUM%) = 0 THEN
                CLOSE BUFNUM%
                KILL FILENAME$
                FSTAT$ = "N"
        ELSE
                CLOSE BUFNUM%
                FSTAT$ = "Y"
        END IF
        IF ERRFLAG% <> 0 THEN GOTO OPENEXIT
        IF MODE$ = "O" AND FSTAT$ = "Y" AND DAFLAG% = 0 THEN
                COLOR EMPH%, 0
                PRINT A$;
                COLOR MESSAGE%, 0
                PRINT " Already Exists! OVERWRITE? (Y/N)";
                CALL INPUTUCASE(AN$)
                IF AN$ = "Y" THEN
                        GOTO OPENIT
                ELSE
                        GOTO OPENFILESTART
                END IF
        END IF
        IF (MODE$ = "I" OR MODE$ = "B") AND FSTAT$ = "N" THEN
                A$ = "N"
                PRINT "File does not exist"
                INPUT "Hit ENTER to continue"; AN$
                PRINT
                IF DAFLAG% = 1 THEN GOTO OPENEXIT
                GOTO OPENFILESTART
        END IF
OPENIT:
        CALL FILESPEC(DRIVE$, A$, FILENAME$)
        OPEN MODE$, BUFNUM%, FILENAME$
OPENEXIT:
        END SUB

